69fb115bab32928d7ce58574cb0120020b5720fe
[linux-3.10.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         /* Initialize default timings to VGA in DVI mode */
45         const struct omap_video_timings default_timings = {
46                 .x_res          = 640,
47                 .y_res          = 480,
48                 .pixel_clock    = 25175,
49                 .hsw            = 96,
50                 .hfp            = 16,
51                 .hbp            = 48,
52                 .vsw            = 2,
53                 .vfp            = 11,
54                 .vbp            = 31,
55
56                 .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
57                 .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
58
59                 .interlace      = false,
60         };
61
62         DSSDBG("ENTER hdmi_panel_probe\n");
63
64         dssdev->panel.timings = default_timings;
65
66         DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
67                 dssdev->panel.timings.x_res,
68                 dssdev->panel.timings.y_res);
69
70         omapdss_hdmi_display_set_timing(dssdev, &dssdev->panel.timings);
71
72         return 0;
73 }
74
75 static void hdmi_panel_remove(struct omap_dss_device *dssdev)
76 {
77
78 }
79
80 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
81 static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
82 {
83         unsigned long flags;
84         int r;
85
86         mutex_lock(&hdmi.lock);
87         spin_lock_irqsave(&hdmi.audio_lock, flags);
88
89         /* enable audio only if the display is active and supports audio */
90         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
91             !hdmi_mode_has_audio()) {
92                 DSSERR("audio not supported or display is off\n");
93                 r = -EPERM;
94                 goto err;
95         }
96
97         r = hdmi_audio_enable();
98
99         if (!r)
100                 dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
101
102 err:
103         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
104         mutex_unlock(&hdmi.lock);
105         return r;
106 }
107
108 static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
109 {
110         unsigned long flags;
111
112         spin_lock_irqsave(&hdmi.audio_lock, flags);
113
114         hdmi_audio_disable();
115
116         dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
117
118         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
119 }
120
121 static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
122 {
123         unsigned long flags;
124         int r;
125
126         spin_lock_irqsave(&hdmi.audio_lock, flags);
127         /*
128          * No need to check the panel state. It was checked when trasitioning
129          * to AUDIO_ENABLED.
130          */
131         if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) {
132                 DSSERR("audio start from invalid state\n");
133                 r = -EPERM;
134                 goto err;
135         }
136
137         r = hdmi_audio_start();
138
139         if (!r)
140                 dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
141
142 err:
143         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
144         return r;
145 }
146
147 static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
148 {
149         unsigned long flags;
150
151         spin_lock_irqsave(&hdmi.audio_lock, flags);
152
153         hdmi_audio_stop();
154         dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
155
156         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
157 }
158
159 static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
160 {
161         bool r = false;
162
163         mutex_lock(&hdmi.lock);
164
165         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
166                 goto err;
167
168         if (!hdmi_mode_has_audio())
169                 goto err;
170
171         r = true;
172 err:
173         mutex_unlock(&hdmi.lock);
174         return r;
175 }
176
177 static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
178                 struct omap_dss_audio *audio)
179 {
180         unsigned long flags;
181         int r;
182
183         mutex_lock(&hdmi.lock);
184         spin_lock_irqsave(&hdmi.audio_lock, flags);
185
186         /* config audio only if the display is active and supports audio */
187         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
188             !hdmi_mode_has_audio()) {
189                 DSSERR("audio not supported or display is off\n");
190                 r = -EPERM;
191                 goto err;
192         }
193
194         r = hdmi_audio_config(audio);
195
196         if (!r)
197                 dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
198
199 err:
200         spin_unlock_irqrestore(&hdmi.audio_lock, flags);
201         mutex_unlock(&hdmi.lock);
202         return r;
203 }
204
205 #else
206 static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
207 {
208         return -EPERM;
209 }
210
211 static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
212 {
213 }
214
215 static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
216 {
217         return -EPERM;
218 }
219
220 static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
221 {
222 }
223
224 static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
225 {
226         return false;
227 }
228
229 static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
230                 struct omap_dss_audio *audio)
231 {
232         return -EPERM;
233 }
234 #endif
235
236 static int hdmi_panel_enable(struct omap_dss_device *dssdev)
237 {
238         int r = 0;
239         DSSDBG("ENTER hdmi_panel_enable\n");
240
241         mutex_lock(&hdmi.lock);
242
243         if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
244                 r = -EINVAL;
245                 goto err;
246         }
247
248         omapdss_hdmi_display_set_timing(dssdev, &dssdev->panel.timings);
249
250         r = omapdss_hdmi_display_enable(dssdev);
251         if (r) {
252                 DSSERR("failed to power on\n");
253                 goto err;
254         }
255
256         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
257
258 err:
259         mutex_unlock(&hdmi.lock);
260
261         return r;
262 }
263
264 static void hdmi_panel_disable(struct omap_dss_device *dssdev)
265 {
266         mutex_lock(&hdmi.lock);
267
268         if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
269                 /*
270                  * TODO: notify audio users that the display was disabled. For
271                  * now, disable audio locally to not break our audio state
272                  * machine.
273                  */
274                 hdmi_panel_audio_disable(dssdev);
275                 omapdss_hdmi_display_disable(dssdev);
276         }
277
278         dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
279
280         mutex_unlock(&hdmi.lock);
281 }
282
283 static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
284 {
285         int r = 0;
286
287         mutex_lock(&hdmi.lock);
288
289         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
290                 r = -EINVAL;
291                 goto err;
292         }
293
294         /*
295          * TODO: notify audio users that the display was suspended. For now,
296          * disable audio locally to not break our audio state machine.
297          */
298         hdmi_panel_audio_disable(dssdev);
299
300         dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
301         omapdss_hdmi_display_disable(dssdev);
302
303 err:
304         mutex_unlock(&hdmi.lock);
305
306         return r;
307 }
308
309 static int hdmi_panel_resume(struct omap_dss_device *dssdev)
310 {
311         int r = 0;
312
313         mutex_lock(&hdmi.lock);
314
315         if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
316                 r = -EINVAL;
317                 goto err;
318         }
319
320         r = omapdss_hdmi_display_enable(dssdev);
321         if (r) {
322                 DSSERR("failed to power on\n");
323                 goto err;
324         }
325         /* TODO: notify audio users that the panel resumed. */
326
327         dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
328
329 err:
330         mutex_unlock(&hdmi.lock);
331
332         return r;
333 }
334
335 static void hdmi_get_timings(struct omap_dss_device *dssdev,
336                         struct omap_video_timings *timings)
337 {
338         mutex_lock(&hdmi.lock);
339
340         *timings = dssdev->panel.timings;
341
342         mutex_unlock(&hdmi.lock);
343 }
344
345 static void hdmi_set_timings(struct omap_dss_device *dssdev,
346                         struct omap_video_timings *timings)
347 {
348         DSSDBG("hdmi_set_timings\n");
349
350         mutex_lock(&hdmi.lock);
351
352         /*
353          * TODO: notify audio users that there was a timings change. For
354          * now, disable audio locally to not break our audio state machine.
355          */
356         hdmi_panel_audio_disable(dssdev);
357
358         omapdss_hdmi_display_set_timing(dssdev, timings);
359         dssdev->panel.timings = *timings;
360
361         mutex_unlock(&hdmi.lock);
362 }
363
364 static int hdmi_check_timings(struct omap_dss_device *dssdev,
365                         struct omap_video_timings *timings)
366 {
367         int r = 0;
368
369         DSSDBG("hdmi_check_timings\n");
370
371         mutex_lock(&hdmi.lock);
372
373         r = omapdss_hdmi_display_check_timing(dssdev, timings);
374
375         mutex_unlock(&hdmi.lock);
376         return r;
377 }
378
379 static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
380 {
381         int r;
382
383         mutex_lock(&hdmi.lock);
384
385         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
386                 r = omapdss_hdmi_display_enable(dssdev);
387                 if (r)
388                         goto err;
389         }
390
391         r = omapdss_hdmi_read_edid(buf, len);
392
393         if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
394                         dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
395                 omapdss_hdmi_display_disable(dssdev);
396 err:
397         mutex_unlock(&hdmi.lock);
398
399         return r;
400 }
401
402 static bool hdmi_detect(struct omap_dss_device *dssdev)
403 {
404         int r;
405
406         mutex_lock(&hdmi.lock);
407
408         if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
409                 r = omapdss_hdmi_display_enable(dssdev);
410                 if (r)
411                         goto err;
412         }
413
414         r = omapdss_hdmi_detect();
415
416         if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
417                         dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
418                 omapdss_hdmi_display_disable(dssdev);
419 err:
420         mutex_unlock(&hdmi.lock);
421
422         return r;
423 }
424
425 static struct omap_dss_driver hdmi_driver = {
426         .probe          = hdmi_panel_probe,
427         .remove         = hdmi_panel_remove,
428         .enable         = hdmi_panel_enable,
429         .disable        = hdmi_panel_disable,
430         .suspend        = hdmi_panel_suspend,
431         .resume         = hdmi_panel_resume,
432         .get_timings    = hdmi_get_timings,
433         .set_timings    = hdmi_set_timings,
434         .check_timings  = hdmi_check_timings,
435         .read_edid      = hdmi_read_edid,
436         .detect         = hdmi_detect,
437         .audio_enable   = hdmi_panel_audio_enable,
438         .audio_disable  = hdmi_panel_audio_disable,
439         .audio_start    = hdmi_panel_audio_start,
440         .audio_stop     = hdmi_panel_audio_stop,
441         .audio_supported        = hdmi_panel_audio_supported,
442         .audio_config   = hdmi_panel_audio_config,
443         .driver                 = {
444                 .name   = "hdmi_panel",
445                 .owner  = THIS_MODULE,
446         },
447 };
448
449 int hdmi_panel_init(void)
450 {
451         mutex_init(&hdmi.lock);
452
453 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
454         spin_lock_init(&hdmi.audio_lock);
455 #endif
456
457         omap_dss_register_driver(&hdmi_driver);
458
459         return 0;
460 }
461
462 void hdmi_panel_exit(void)
463 {
464         omap_dss_unregister_driver(&hdmi_driver);
465
466 }