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