Merge remote-tracking branch 'upstream' into next
[cascardo/linux.git] / drivers / video / omap2 / dss / hdmi_panel.c
1 /*
2  * hdmi_panel.c
3  *
4  * HDMI library support functions for TI OMAP4 processors.
5  *
6  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
7  * Authors:     Mythri P k <mythripk@ti.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License version 2 as published by
11  * the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <linux/kernel.h>
23 #include <linux/err.h>
24 #include <linux/io.h>
25 #include <linux/mutex.h>
26 #include <linux/module.h>
27 #include <video/omapdss.h>
28 #include <linux/slab.h>
29
30 #include "dss.h"
31
32 static struct {
33         /* This protects the panel ops, mainly when accessing the HDMI IP. */
34         struct mutex lock;
35 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
36         /* This protects the audio ops, specifically. */
37         spinlock_t audio_lock;
38 #endif
39 } hdmi;
40
41
42 static int hdmi_panel_probe(struct omap_dss_device *dssdev)
43 {
44         DSSDBG("ENTER hdmi_panel_probe\n");
45
46         dssdev->panel.timings = (struct omap_video_timings)
47                         { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
48                                 OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
49                                 false,
50                         };
51
52         DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
53                 dssdev->panel.timings.x_res,
54                 dssdev->panel.timings.y_res);
55         return 0;
56 }
57
58 static void hdmi_panel_remove(struct omap_dss_device *dssdev)
59 {
60
61 }
62
63 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
64 static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
65 {
66         unsigned long flags;
67         int r;
68
69         mutex_lock(&hdmi.lock);
70         spin_lock_irqsave(&hdmi.audio_lock, flags);
71
72         /* enable audio only if the display is active and supports audio */
73         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
74             !hdmi_mode_has_audio()) {
75                 DSSERR("audio not supported or display is off\n");
76                 r = -EPERM;
77                 goto err;
78         }
79
80         r = hdmi_audio_enable();
81
82         if (!r)
83                 dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
84
85 err:
86         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
87         mutex_unlock(&hdmi.lock);
88         return r;
89 }
90
91 static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
92 {
93         unsigned long flags;
94
95         spin_lock_irqsave(&hdmi.audio_lock, flags);
96
97         hdmi_audio_disable();
98
99         dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
100
101         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
102 }
103
104 static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
105 {
106         unsigned long flags;
107         int r;
108
109         spin_lock_irqsave(&hdmi.audio_lock, flags);
110         /*
111          * No need to check the panel state. It was checked when trasitioning
112          * to AUDIO_ENABLED.
113          */
114         if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) {
115                 DSSERR("audio start from invalid state\n");
116                 r = -EPERM;
117                 goto err;
118         }
119
120         r = hdmi_audio_start();
121
122         if (!r)
123                 dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
124
125 err:
126         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
127         return r;
128 }
129
130 static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
131 {
132         unsigned long flags;
133
134         spin_lock_irqsave(&hdmi.audio_lock, flags);
135
136         hdmi_audio_stop();
137         dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
138
139         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
140 }
141
142 static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
143 {
144         bool r = false;
145
146         mutex_lock(&hdmi.lock);
147
148         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
149                 goto err;
150
151         if (!hdmi_mode_has_audio())
152                 goto err;
153
154         r = true;
155 err:
156         mutex_unlock(&hdmi.lock);
157         return r;
158 }
159
160 static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
161                 struct omap_dss_audio *audio)
162 {
163         unsigned long flags;
164         int r;
165
166         mutex_lock(&hdmi.lock);
167         spin_lock_irqsave(&hdmi.audio_lock, flags);
168
169         /* config audio only if the display is active and supports audio */
170         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
171             !hdmi_mode_has_audio()) {
172                 DSSERR("audio not supported or display is off\n");
173                 r = -EPERM;
174                 goto err;
175         }
176
177         r = hdmi_audio_config(audio);
178
179         if (!r)
180                 dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
181
182 err:
183         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
184         mutex_unlock(&hdmi.lock);
185         return r;
186 }
187
188 #else
189 static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
190 {
191         return -EPERM;
192 }
193
194 static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
195 {
196 }
197
198 static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
199 {
200         return -EPERM;
201 }
202
203 static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
204 {
205 }
206
207 static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
208 {
209         return false;
210 }
211
212 static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
213                 struct omap_dss_audio *audio)
214 {
215         return -EPERM;
216 }
217 #endif
218
219 static int hdmi_panel_enable(struct omap_dss_device *dssdev)
220 {
221         int r = 0;
222         DSSDBG("ENTER hdmi_panel_enable\n");
223
224         mutex_lock(&hdmi.lock);
225
226         if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
227                 r = -EINVAL;
228                 goto err;
229         }
230
231         r = omapdss_hdmi_display_enable(dssdev);
232         if (r) {
233                 DSSERR("failed to power on\n");
234                 goto err;
235         }
236
237         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
238
239 err:
240         mutex_unlock(&hdmi.lock);
241
242         return r;
243 }
244
245 static void hdmi_panel_disable(struct omap_dss_device *dssdev)
246 {
247         mutex_lock(&hdmi.lock);
248
249         if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
250                 /*
251                  * TODO: notify audio users that the display was disabled. For
252                  * now, disable audio locally to not break our audio state
253                  * machine.
254                  */
255                 hdmi_panel_audio_disable(dssdev);
256                 omapdss_hdmi_display_disable(dssdev);
257         }
258
259         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
260
261         mutex_unlock(&hdmi.lock);
262 }
263
264 static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
265 {
266         int r = 0;
267
268         mutex_lock(&hdmi.lock);
269
270         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
271                 r = -EINVAL;
272                 goto err;
273         }
274
275         /*
276          * TODO: notify audio users that the display was suspended. For now,
277          * disable audio locally to not break our audio state machine.
278          */
279         hdmi_panel_audio_disable(dssdev);
280
281         dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
282         omapdss_hdmi_display_disable(dssdev);
283
284 err:
285         mutex_unlock(&hdmi.lock);
286
287         return r;
288 }
289
290 static int hdmi_panel_resume(struct omap_dss_device *dssdev)
291 {
292         int r = 0;
293
294         mutex_lock(&hdmi.lock);
295
296         if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
297                 r = -EINVAL;
298                 goto err;
299         }
300
301         r = omapdss_hdmi_display_enable(dssdev);
302         if (r) {
303                 DSSERR("failed to power on\n");
304                 goto err;
305         }
306         /* TODO: notify audio users that the panel resumed. */
307
308         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
309
310 err:
311         mutex_unlock(&hdmi.lock);
312
313         return r;
314 }
315
316 static void hdmi_get_timings(struct omap_dss_device *dssdev,
317                         struct omap_video_timings *timings)
318 {
319         mutex_lock(&hdmi.lock);
320
321         *timings = dssdev->panel.timings;
322
323         mutex_unlock(&hdmi.lock);
324 }
325
326 static void hdmi_set_timings(struct omap_dss_device *dssdev,
327                         struct omap_video_timings *timings)
328 {
329         DSSDBG("hdmi_set_timings\n");
330
331         mutex_lock(&hdmi.lock);
332
333         /*
334          * TODO: notify audio users that there was a timings change. For
335          * now, disable audio locally to not break our audio state machine.
336          */
337         hdmi_panel_audio_disable(dssdev);
338
339         dssdev->panel.timings = *timings;
340         omapdss_hdmi_display_set_timing(dssdev);
341
342         mutex_unlock(&hdmi.lock);
343 }
344
345 static int hdmi_check_timings(struct omap_dss_device *dssdev,
346                         struct omap_video_timings *timings)
347 {
348         int r = 0;
349
350         DSSDBG("hdmi_check_timings\n");
351
352         mutex_lock(&hdmi.lock);
353
354         r = omapdss_hdmi_display_check_timing(dssdev, timings);
355
356         mutex_unlock(&hdmi.lock);
357         return r;
358 }
359
360 static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
361 {
362         int r;
363
364         mutex_lock(&hdmi.lock);
365
366         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
367                 r = omapdss_hdmi_display_enable(dssdev);
368                 if (r)
369                         goto err;
370         }
371
372         r = omapdss_hdmi_read_edid(buf, len);
373
374         if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
375                         dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
376                 omapdss_hdmi_display_disable(dssdev);
377 err:
378         mutex_unlock(&hdmi.lock);
379
380         return r;
381 }
382
383 static bool hdmi_detect(struct omap_dss_device *dssdev)
384 {
385         int r;
386
387         mutex_lock(&hdmi.lock);
388
389         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
390                 r = omapdss_hdmi_display_enable(dssdev);
391                 if (r)
392                         goto err;
393         }
394
395         r = omapdss_hdmi_detect();
396
397         if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
398                         dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
399                 omapdss_hdmi_display_disable(dssdev);
400 err:
401         mutex_unlock(&hdmi.lock);
402
403         return r;
404 }
405
406 static struct omap_dss_driver hdmi_driver = {
407         .probe          = hdmi_panel_probe,
408         .remove         = hdmi_panel_remove,
409         .enable         = hdmi_panel_enable,
410         .disable        = hdmi_panel_disable,
411         .suspend        = hdmi_panel_suspend,
412         .resume         = hdmi_panel_resume,
413         .get_timings    = hdmi_get_timings,
414         .set_timings    = hdmi_set_timings,
415         .check_timings  = hdmi_check_timings,
416         .read_edid      = hdmi_read_edid,
417         .detect         = hdmi_detect,
418         .audio_enable   = hdmi_panel_audio_enable,
419         .audio_disable  = hdmi_panel_audio_disable,
420         .audio_start    = hdmi_panel_audio_start,
421         .audio_stop     = hdmi_panel_audio_stop,
422         .audio_supported        = hdmi_panel_audio_supported,
423         .audio_config   = hdmi_panel_audio_config,
424         .driver                 = {
425                 .name   = "hdmi_panel",
426                 .owner  = THIS_MODULE,
427         },
428 };
429
430 int hdmi_panel_init(void)
431 {
432         mutex_init(&hdmi.lock);
433
434 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
435         spin_lock_init(&hdmi.audio_lock);
436 #endif
437
438         omap_dss_register_driver(&hdmi_driver);
439
440         return 0;
441 }
442
443 void hdmi_panel_exit(void)
444 {
445         omap_dss_unregister_driver(&hdmi_driver);
446
447 }