]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/video/omap2/dss/hdmi_panel.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[karo-tx-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         /* 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 void hdmi_get_timings(struct omap_dss_device *dssdev,
284                         struct omap_video_timings *timings)
285 {
286         mutex_lock(&hdmi.lock);
287
288         *timings = dssdev->panel.timings;
289
290         mutex_unlock(&hdmi.lock);
291 }
292
293 static void hdmi_set_timings(struct omap_dss_device *dssdev,
294                         struct omap_video_timings *timings)
295 {
296         DSSDBG("hdmi_set_timings\n");
297
298         mutex_lock(&hdmi.lock);
299
300         /*
301          * TODO: notify audio users that there was a timings change. For
302          * now, disable audio locally to not break our audio state machine.
303          */
304         hdmi_panel_audio_disable(dssdev);
305
306         omapdss_hdmi_display_set_timing(dssdev, timings);
307         dssdev->panel.timings = *timings;
308
309         mutex_unlock(&hdmi.lock);
310 }
311
312 static int hdmi_check_timings(struct omap_dss_device *dssdev,
313                         struct omap_video_timings *timings)
314 {
315         int r = 0;
316
317         DSSDBG("hdmi_check_timings\n");
318
319         mutex_lock(&hdmi.lock);
320
321         r = omapdss_hdmi_display_check_timing(dssdev, timings);
322
323         mutex_unlock(&hdmi.lock);
324         return r;
325 }
326
327 static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
328 {
329         int r;
330         bool need_enable;
331
332         mutex_lock(&hdmi.lock);
333
334         need_enable = dssdev->state == OMAP_DSS_DISPLAY_DISABLED;
335
336         if (need_enable) {
337                 r = omapdss_hdmi_core_enable(dssdev);
338                 if (r)
339                         goto err;
340         }
341
342         r = omapdss_hdmi_read_edid(buf, len);
343
344         if (need_enable)
345                 omapdss_hdmi_core_disable(dssdev);
346 err:
347         mutex_unlock(&hdmi.lock);
348
349         return r;
350 }
351
352 static bool hdmi_detect(struct omap_dss_device *dssdev)
353 {
354         int r;
355         bool need_enable;
356
357         mutex_lock(&hdmi.lock);
358
359         need_enable = dssdev->state == OMAP_DSS_DISPLAY_DISABLED;
360
361         if (need_enable) {
362                 r = omapdss_hdmi_core_enable(dssdev);
363                 if (r)
364                         goto err;
365         }
366
367         r = omapdss_hdmi_detect();
368
369         if (need_enable)
370                 omapdss_hdmi_core_disable(dssdev);
371 err:
372         mutex_unlock(&hdmi.lock);
373
374         return r;
375 }
376
377 static struct omap_dss_driver hdmi_driver = {
378         .probe          = hdmi_panel_probe,
379         .remove         = hdmi_panel_remove,
380         .enable         = hdmi_panel_enable,
381         .disable        = hdmi_panel_disable,
382         .get_timings    = hdmi_get_timings,
383         .set_timings    = hdmi_set_timings,
384         .check_timings  = hdmi_check_timings,
385         .read_edid      = hdmi_read_edid,
386         .detect         = hdmi_detect,
387         .audio_enable   = hdmi_panel_audio_enable,
388         .audio_disable  = hdmi_panel_audio_disable,
389         .audio_start    = hdmi_panel_audio_start,
390         .audio_stop     = hdmi_panel_audio_stop,
391         .audio_supported        = hdmi_panel_audio_supported,
392         .audio_config   = hdmi_panel_audio_config,
393         .driver                 = {
394                 .name   = "hdmi_panel",
395                 .owner  = THIS_MODULE,
396         },
397 };
398
399 int hdmi_panel_init(void)
400 {
401         mutex_init(&hdmi.lock);
402
403 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
404         spin_lock_init(&hdmi.audio_lock);
405 #endif
406
407         return omap_dss_register_driver(&hdmi_driver);
408 }
409
410 void hdmi_panel_exit(void)
411 {
412         omap_dss_unregister_driver(&hdmi_driver);
413
414 }