diff options
author | Ricardo Neri <ricardo.neri@ti.com> | 2012-05-09 22:09:50 -0400 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2012-05-11 08:17:10 -0400 |
commit | f3a97491f2c54e5239663f8f147b34c280cf7db5 (patch) | |
tree | 8d502829f3b27b5d8b587fb965919260d832497a /drivers/video | |
parent | b7dea05aec8b6421500929d5003866655d6d9dab (diff) |
OMAPDSS: HDMI: Implement DSS driver interface for audio
Implement the DSS device driver audio support interface in the HDMI
panel driver and generic driver. The implementation relies on the
IP-specific functions that are defined at DSS probe time.
A mixed locking strategy is used. The panel's mutex is used when
the state of the panel is queried as required by the audio functions.
The audio state is protected using a spinlock as users of DSS HDMI
audio functionality might start/stop audio while holding a spinlock.
The mutex and the spinlock are held and released as needed by each
individual function to protect the panel state and the audio state.
Although the panel's audio_start functions does not check whether
the panel is active, the audio _ENABLED state can be reached only
from audio_enable, which does check the state of the panel. Also,
if the panel is ever disabled, the audio state will transition
to _DISABLED. Transitions are always protected by the audio lock.
Signed-off-by: Ricardo Neri <ricardo.neri@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/omap2/dss/dss.h | 8 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi.c | 42 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi_panel.c | 193 |
3 files changed, 241 insertions, 2 deletions
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index d5cb19fe7e8b..4aa9529a4768 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h | |||
@@ -464,6 +464,14 @@ int omapdss_hdmi_read_edid(u8 *buf, int len); | |||
464 | bool omapdss_hdmi_detect(void); | 464 | bool omapdss_hdmi_detect(void); |
465 | int hdmi_panel_init(void); | 465 | int hdmi_panel_init(void); |
466 | void hdmi_panel_exit(void); | 466 | void hdmi_panel_exit(void); |
467 | #ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO | ||
468 | int hdmi_audio_enable(void); | ||
469 | void hdmi_audio_disable(void); | ||
470 | int hdmi_audio_start(void); | ||
471 | void hdmi_audio_stop(void); | ||
472 | bool hdmi_mode_has_audio(void); | ||
473 | int hdmi_audio_config(struct omap_dss_audio *audio); | ||
474 | #endif | ||
467 | 475 | ||
468 | /* RFBI */ | 476 | /* RFBI */ |
469 | int rfbi_init_platform_driver(void) __init; | 477 | int rfbi_init_platform_driver(void) __init; |
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 09fdbf8714ea..8195c7166d20 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c | |||
@@ -654,6 +654,48 @@ int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) | |||
654 | 654 | ||
655 | return 0; | 655 | return 0; |
656 | } | 656 | } |
657 | |||
658 | int hdmi_audio_enable(void) | ||
659 | { | ||
660 | DSSDBG("audio_enable\n"); | ||
661 | |||
662 | return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); | ||
663 | } | ||
664 | |||
665 | void hdmi_audio_disable(void) | ||
666 | { | ||
667 | DSSDBG("audio_disable\n"); | ||
668 | |||
669 | hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); | ||
670 | } | ||
671 | |||
672 | int hdmi_audio_start(void) | ||
673 | { | ||
674 | DSSDBG("audio_start\n"); | ||
675 | |||
676 | return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); | ||
677 | } | ||
678 | |||
679 | void hdmi_audio_stop(void) | ||
680 | { | ||
681 | DSSDBG("audio_stop\n"); | ||
682 | |||
683 | hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); | ||
684 | } | ||
685 | |||
686 | bool hdmi_mode_has_audio(void) | ||
687 | { | ||
688 | if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) | ||
689 | return true; | ||
690 | else | ||
691 | return false; | ||
692 | } | ||
693 | |||
694 | int hdmi_audio_config(struct omap_dss_audio *audio) | ||
695 | { | ||
696 | return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); | ||
697 | } | ||
698 | |||
657 | #endif | 699 | #endif |
658 | 700 | ||
659 | static void __init hdmi_probe_pdata(struct platform_device *pdev) | 701 | static void __init hdmi_probe_pdata(struct platform_device *pdev) |
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 5e215d6e8d0a..1179e3c4b1c7 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c | |||
@@ -32,6 +32,10 @@ | |||
32 | static struct { | 32 | static struct { |
33 | /* This protects the panel ops, mainly when accessing the HDMI IP. */ | 33 | /* This protects the panel ops, mainly when accessing the HDMI IP. */ |
34 | struct mutex lock; | 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 | ||
35 | } hdmi; | 39 | } hdmi; |
36 | 40 | ||
37 | 41 | ||
@@ -55,6 +59,162 @@ static void hdmi_panel_remove(struct omap_dss_device *dssdev) | |||
55 | 59 | ||
56 | } | 60 | } |
57 | 61 | ||
62 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||
63 | static 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 | |||
84 | err: | ||
85 | spin_unlock_irqrestore(&hdmi.audio_lock, flags); | ||
86 | mutex_unlock(&hdmi.lock); | ||
87 | return r; | ||
88 | } | ||
89 | |||
90 | static 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 | |||
103 | static 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 | |||
124 | err: | ||
125 | spin_unlock_irqrestore(&hdmi.audio_lock, flags); | ||
126 | return r; | ||
127 | } | ||
128 | |||
129 | static 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 | |||
141 | static 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; | ||
154 | err: | ||
155 | mutex_unlock(&hdmi.lock); | ||
156 | return r; | ||
157 | } | ||
158 | |||
159 | static 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 | |||
181 | err: | ||
182 | spin_unlock_irqrestore(&hdmi.audio_lock, flags); | ||
183 | mutex_unlock(&hdmi.lock); | ||
184 | return r; | ||
185 | } | ||
186 | |||
187 | #else | ||
188 | static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) | ||
189 | { | ||
190 | return -EPERM; | ||
191 | } | ||
192 | |||
193 | static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) | ||
194 | { | ||
195 | } | ||
196 | |||
197 | static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) | ||
198 | { | ||
199 | return -EPERM; | ||
200 | } | ||
201 | |||
202 | static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) | ||
203 | { | ||
204 | } | ||
205 | |||
206 | static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) | ||
207 | { | ||
208 | return false; | ||
209 | } | ||
210 | |||
211 | static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, | ||
212 | struct omap_dss_audio *audio) | ||
213 | { | ||
214 | return -EPERM; | ||
215 | } | ||
216 | #endif | ||
217 | |||
58 | static int hdmi_panel_enable(struct omap_dss_device *dssdev) | 218 | static int hdmi_panel_enable(struct omap_dss_device *dssdev) |
59 | { | 219 | { |
60 | int r = 0; | 220 | int r = 0; |
@@ -85,8 +245,15 @@ static void hdmi_panel_disable(struct omap_dss_device *dssdev) | |||
85 | { | 245 | { |
86 | mutex_lock(&hdmi.lock); | 246 | mutex_lock(&hdmi.lock); |
87 | 247 | ||
88 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | 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); | ||
89 | omapdss_hdmi_display_disable(dssdev); | 255 | omapdss_hdmi_display_disable(dssdev); |
256 | } | ||
90 | 257 | ||
91 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | 258 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
92 | 259 | ||
@@ -104,8 +271,13 @@ static int hdmi_panel_suspend(struct omap_dss_device *dssdev) | |||
104 | goto err; | 271 | goto err; |
105 | } | 272 | } |
106 | 273 | ||
107 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | 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); | ||
108 | 279 | ||
280 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | ||
109 | omapdss_hdmi_display_disable(dssdev); | 281 | omapdss_hdmi_display_disable(dssdev); |
110 | 282 | ||
111 | err: | 283 | err: |
@@ -130,6 +302,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) | |||
130 | DSSERR("failed to power on\n"); | 302 | DSSERR("failed to power on\n"); |
131 | goto err; | 303 | goto err; |
132 | } | 304 | } |
305 | /* TODO: notify audio users that the panel resumed. */ | ||
133 | 306 | ||
134 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | 307 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
135 | 308 | ||
@@ -156,6 +329,12 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, | |||
156 | 329 | ||
157 | mutex_lock(&hdmi.lock); | 330 | mutex_lock(&hdmi.lock); |
158 | 331 | ||
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 | |||
159 | dssdev->panel.timings = *timings; | 338 | dssdev->panel.timings = *timings; |
160 | omapdss_hdmi_display_set_timing(dssdev); | 339 | omapdss_hdmi_display_set_timing(dssdev); |
161 | 340 | ||
@@ -235,6 +414,12 @@ static struct omap_dss_driver hdmi_driver = { | |||
235 | .check_timings = hdmi_check_timings, | 414 | .check_timings = hdmi_check_timings, |
236 | .read_edid = hdmi_read_edid, | 415 | .read_edid = hdmi_read_edid, |
237 | .detect = hdmi_detect, | 416 | .detect = hdmi_detect, |
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, | ||
238 | .driver = { | 423 | .driver = { |
239 | .name = "hdmi_panel", | 424 | .name = "hdmi_panel", |
240 | .owner = THIS_MODULE, | 425 | .owner = THIS_MODULE, |
@@ -245,6 +430,10 @@ int hdmi_panel_init(void) | |||
245 | { | 430 | { |
246 | mutex_init(&hdmi.lock); | 431 | mutex_init(&hdmi.lock); |
247 | 432 | ||
433 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||
434 | spin_lock_init(&hdmi.audio_lock); | ||
435 | #endif | ||
436 | |||
248 | omap_dss_register_driver(&hdmi_driver); | 437 | omap_dss_register_driver(&hdmi_driver); |
249 | 438 | ||
250 | return 0; | 439 | return 0; |