c38e54363ca0207f691c557945060fb57c7664de
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rk_hdmi_task.c
1 #include <linux/kernel.h>
2 #include <linux/delay.h>
3 #include <linux/interrupt.h>
4 #include <linux/rockchip/common.h>
5 #include <dt-bindings/clock/rk_system_status.h>
6 #include "rk_hdmi.h"
7
8 #define HDMI_MAX_TRY_TIMES      1
9 #define HDMI_MAX_ID 1
10
11 static char *envp[] = { "INTERFACE=HDMI", NULL };
12
13 static void hdmi_sys_show_state(struct hdmi *hdmi)
14 {
15         switch (hdmi->state) {
16         case HDMI_SLEEP:
17                 hdmi_dbg(hdmi->dev, "HDMI_SLEEP\n");
18                 break;
19         case HDMI_INITIAL:
20                 hdmi_dbg(hdmi->dev, "HDMI_INITIAL\n");
21                 break;
22         case WAIT_HOTPLUG:
23                 hdmi_dbg(hdmi->dev, "WAIT_HOTPLUG\n");
24                 break;
25         case READ_PARSE_EDID:
26                 hdmi_dbg(hdmi->dev, "READ_PARSE_EDID\n");
27                 break;
28         case WAIT_HDMI_ENABLE:
29                 hdmi_dbg(hdmi->dev, "WAIT_HDMI_ENABLE\n");
30                 break;
31         case SYSTEM_CONFIG:
32                 hdmi_dbg(hdmi->dev, "SYSTEM_CONFIG\n");
33                 break;
34         case CONFIG_VIDEO:
35                 hdmi_dbg(hdmi->dev, "CONFIG_VIDEO\n");
36                 break;
37         case CONFIG_AUDIO:
38                 hdmi_dbg(hdmi->dev, "CONFIG_AUDIO\n");
39                 break;
40         case PLAY_BACK:
41                 hdmi_dbg(hdmi->dev, "PLAY_BACK\n");
42                 break;
43         default:
44                 hdmi_dbg(hdmi->dev, "Unkown State %d\n", hdmi->state);
45                 break;
46         }
47 }
48
49 int hdmi_sys_init(struct hdmi *hdmi)
50 {
51         hdmi->hotplug = HDMI_HPD_REMOVED;
52         hdmi->state = HDMI_SLEEP;
53         hdmi->enable = HDMI_ENABLE;
54         hdmi->autoconfig = HDMI_AUTO_CONFIGURE;
55         hdmi->display = HDMI_DISABLE;
56
57         hdmi->vic = HDMI_VIDEO_DEFAULT_MODE;
58         hdmi->audio.channel = HDMI_AUDIO_DEFAULT_CHANNEL;
59         hdmi->audio.rate = HDMI_AUDIO_DEFAULT_RATE;
60         hdmi->audio.word_length = HDMI_AUDIO_DEFAULT_WORD_LENGTH;
61
62         memset(&hdmi->edid, 0, sizeof(struct hdmi_edid));
63         INIT_LIST_HEAD(&hdmi->edid.modelist);
64         return 0;
65 }
66
67 void hdmi_sys_remove(struct hdmi *hdmi)
68 {
69         int audio_need;
70
71         audio_need = hdmi->edid.base_audio_support == 1
72             && hdmi->edid.sink_hdmi == 1;
73
74         fb_destroy_modelist(&hdmi->edid.modelist);
75         kfree(hdmi->edid.audio);
76         if (hdmi->edid.specs) {
77                 kfree(hdmi->edid.specs->modedb);
78                 kfree(hdmi->edid.specs);
79         }
80         memset(&hdmi->edid, 0, sizeof(struct hdmi_edid));
81         INIT_LIST_HEAD(&hdmi->edid.modelist);
82         hdmi->display = HDMI_DISABLE;
83         if (hdmi->set_vif)
84                 hdmi->set_vif(hdmi, hdmi->lcdc->screen1, 0);
85         rk_fb_switch_screen(hdmi->lcdc->screen1, 0, hdmi->lcdc->id);
86         kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp);
87
88 #ifdef CONFIG_SWITCH
89         if (audio_need)
90                 switch_set_state(&(hdmi->switch_hdmi), 0);
91 #endif
92         rockchip_clear_system_status(SYS_STATUS_HDMI);
93 }
94
95 static void hdmi_sys_sleep(struct hdmi *hdmi)
96 {
97         mutex_lock(&hdmi->enable_mutex);
98         if (hdmi->enable && hdmi->irq)
99                 disable_irq(hdmi->irq);
100         hdmi->state = HDMI_SLEEP;
101         hdmi->remove(hdmi);
102         if (hdmi->enable && hdmi->irq)
103                 enable_irq(hdmi->irq);
104         mutex_unlock(&hdmi->enable_mutex);
105 }
106
107 static int hdmi_process_command(struct hdmi *hdmi)
108 {
109         int change, state = hdmi->state;
110
111         change = hdmi->command;
112         if (change != HDMI_CONFIG_NONE) {
113                 hdmi->command = HDMI_CONFIG_NONE;
114                 switch (change) {
115                 case HDMI_CONFIG_ENABLE:
116                         /* disable HDMI */
117                         mutex_lock(&hdmi->enable_mutex);
118                         if (!hdmi->enable || hdmi->suspend) {
119                                 if (hdmi->hotplug != HDMI_HPD_REMOVED) {
120                                         hdmi->hotplug = HDMI_HPD_REMOVED;
121                                         hdmi_sys_remove(hdmi);
122                                 }
123                                 hdmi->state = HDMI_SLEEP;
124                                 hdmi->remove(hdmi);
125                                 state = HDMI_SLEEP;
126                         }
127                         mutex_unlock(&hdmi->enable_mutex);
128                         if (hdmi->wait == 1) {
129                                 complete(&hdmi->complete);
130                                 hdmi->wait = 0;
131                         }
132                         break;
133                 case HDMI_CONFIG_COLOR:
134                         if (state > CONFIG_VIDEO)
135                                 state = CONFIG_VIDEO;
136                         break;
137                 case HDMI_CONFIG_HDCP:
138                         break;
139                 case HDMI_CONFIG_DISPLAY:
140                         break;
141                 case HDMI_CONFIG_AUDIO:
142                         if (state > CONFIG_AUDIO)
143                                 state = CONFIG_AUDIO;
144                         break;
145                 case HDMI_CONFIG_VIDEO:
146                 default:
147                         if (state > SYSTEM_CONFIG)
148                                 state = SYSTEM_CONFIG;
149                         else {
150                                 if (hdmi->wait == 1) {
151                                         complete(&hdmi->complete);
152                                         hdmi->wait = 0;
153                                 }
154                         }
155                         break;
156                 }
157         } else if (state == HDMI_SLEEP)
158                 state = WAIT_HOTPLUG;
159         return state;
160 }
161
162 static DEFINE_MUTEX(work_mutex);
163
164 void hdmi_work(struct work_struct *work)
165 {
166         int hotplug, state_last;
167         int rc = HDMI_ERROR_SUCESS, trytimes = 0;
168         struct hdmi_video_para video;
169         struct delayed_work *delay_work =
170             container_of(work, struct delayed_work, work);
171         struct hdmi *hdmi = container_of(delay_work, struct hdmi, delay_work);
172
173         mutex_lock(&work_mutex);
174         /* Process hdmi command */
175         hdmi->state = hdmi_process_command(hdmi);
176
177         if (!hdmi->enable || hdmi->suspend) {
178                 mutex_unlock(&work_mutex);
179                 return;
180         }
181         hotplug = hdmi->detect_hotplug(hdmi);
182         hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __func__,
183                  hotplug, hdmi->hotplug);
184
185         if (hotplug != hdmi->hotplug) {
186                 if (hotplug == HDMI_HPD_ACTIVED) {
187                         if (hdmi->insert)
188                                 hdmi->insert(hdmi);
189                         hdmi->state = READ_PARSE_EDID;
190                 } else if (hdmi->hotplug == HDMI_HPD_ACTIVED) {
191                         hdmi->hotplug = hotplug;
192                         hdmi_sys_remove(hdmi);
193                         if (hotplug == HDMI_HPD_REMOVED)
194                                 hdmi_sys_sleep(hdmi);
195                         else {
196                                 hdmi->state = WAIT_HOTPLUG;
197                                 hdmi->remove(hdmi);
198                         }
199                         if (hdmi->wait == 1) {
200                                 complete(&hdmi->complete);
201                                 hdmi->wait = 0;
202                         }
203                         mutex_unlock(&work_mutex);
204                         return;
205                 } else if (hotplug == HDMI_HPD_REMOVED) {
206                         hdmi->state = HDMI_SLEEP;
207                         hdmi->remove(hdmi);
208                 }
209                 hdmi->hotplug = hotplug;
210         } else if (hotplug == HDMI_HPD_REMOVED)
211                 hdmi_sys_sleep(hdmi);
212
213         do {
214                 hdmi_sys_show_state(hdmi);
215                 state_last = hdmi->state;
216                 switch (hdmi->state) {
217                 case READ_PARSE_EDID:
218                         rc = hdmi_sys_parse_edid(hdmi);
219                         if (rc == HDMI_ERROR_SUCESS) {
220                                 hdmi->state = SYSTEM_CONFIG;
221                                 kobject_uevent_env(&hdmi->dev->kobj, KOBJ_ADD,
222                                                    envp);
223                                 hdmi_dbg(hdmi->dev,
224                                          "[%s] base_audio_support =%d,sink_hdmi = %d\n",
225                                          __func__,
226                                          hdmi->edid.base_audio_support,
227                                          hdmi->edid.sink_hdmi);
228 #ifdef CONFIG_SWITCH
229                                 if (hdmi->edid.base_audio_support == 1
230                                     && hdmi->edid.sink_hdmi == 1)
231                                         switch_set_state(&(hdmi->switch_hdmi),
232                                                          1);
233 #endif
234                                 rockchip_set_system_status(SYS_STATUS_HDMI);
235                         }
236                         break;
237                 case SYSTEM_CONFIG:
238 #ifdef CONFIG_HDMI_RK616
239                         hdmi->remove(hdmi);
240 #endif
241                         if (hdmi->autoconfig)
242                                 hdmi->vic = hdmi_find_best_mode(hdmi, 0);
243                         else
244                                 hdmi->vic =
245                                     hdmi_find_best_mode(hdmi, hdmi->vic);
246                         rc = hdmi_switch_fb(hdmi, hdmi->vic);
247                         if (rc == HDMI_ERROR_SUCESS)
248                                 hdmi->state = CONFIG_VIDEO;
249                         break;
250                 case CONFIG_VIDEO:
251                         hdmi->display = HDMI_DISABLE;
252                         hdmi_init_video_para(hdmi, &video);
253                         rc = hdmi->config_video(hdmi, &video);
254                         if (rc == HDMI_ERROR_SUCESS) {
255                                 if (hdmi->edid.sink_hdmi)
256                                         hdmi->state = CONFIG_AUDIO;
257                                 else
258                                         hdmi->state = PLAY_BACK;
259                         }
260                         break;
261                 case CONFIG_AUDIO:
262                         rc = hdmi->config_audio(hdmi, &(hdmi->audio));
263
264                         if (rc == HDMI_ERROR_SUCESS)
265                                 hdmi->state = PLAY_BACK;
266                         break;
267                 case PLAY_BACK:
268                         if (hdmi->display != HDMI_ENABLE) {
269                                 hdmi->control_output(hdmi, HDMI_ENABLE);
270                                 hdmi->display = HDMI_ENABLE;
271                                 if (hdmi->hdcp_cb)
272                                         hdmi->hdcp_cb();
273                         }
274
275                         if (hdmi->wait == 1) {
276                                 complete(&hdmi->complete);
277                                 hdmi->wait = 0;
278                         }
279                         break;
280                 default:
281                         break;
282                 }
283                 if (rc != HDMI_ERROR_SUCESS) {
284                         trytimes++;
285                         msleep(20);
286                 }
287                 if (hdmi->state != state_last)
288                         trytimes = 0;
289
290         } while ((hdmi->state != state_last || (rc != HDMI_ERROR_SUCESS))
291                  && trytimes < HDMI_MAX_TRY_TIMES);
292
293         hdmi_dbg(hdmi->dev, "[%s] done\n", __func__);
294         mutex_unlock(&work_mutex);
295 }