diff options
Diffstat (limited to 'drivers/video/omap2/dss/hdmi_panel.c')
-rw-r--r-- | drivers/video/omap2/dss/hdmi_panel.c | 236 |
1 files changed, 213 insertions, 23 deletions
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 533d5dc634d2..1179e3c4b1c7 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c | |||
@@ -30,7 +30,12 @@ | |||
30 | #include "dss.h" | 30 | #include "dss.h" |
31 | 31 | ||
32 | static struct { | 32 | static struct { |
33 | struct mutex hdmi_lock; | 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 | ||
34 | } hdmi; | 39 | } hdmi; |
35 | 40 | ||
36 | 41 | ||
@@ -54,12 +59,168 @@ static void hdmi_panel_remove(struct omap_dss_device *dssdev) | |||
54 | 59 | ||
55 | } | 60 | } |
56 | 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 | |||
57 | static int hdmi_panel_enable(struct omap_dss_device *dssdev) | 218 | static int hdmi_panel_enable(struct omap_dss_device *dssdev) |
58 | { | 219 | { |
59 | int r = 0; | 220 | int r = 0; |
60 | DSSDBG("ENTER hdmi_panel_enable\n"); | 221 | DSSDBG("ENTER hdmi_panel_enable\n"); |
61 | 222 | ||
62 | mutex_lock(&hdmi.hdmi_lock); | 223 | mutex_lock(&hdmi.lock); |
63 | 224 | ||
64 | if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { | 225 | if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { |
65 | r = -EINVAL; | 226 | r = -EINVAL; |
@@ -75,40 +236,52 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev) | |||
75 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | 236 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
76 | 237 | ||
77 | err: | 238 | err: |
78 | mutex_unlock(&hdmi.hdmi_lock); | 239 | mutex_unlock(&hdmi.lock); |
79 | 240 | ||
80 | return r; | 241 | return r; |
81 | } | 242 | } |
82 | 243 | ||
83 | static void hdmi_panel_disable(struct omap_dss_device *dssdev) | 244 | static void hdmi_panel_disable(struct omap_dss_device *dssdev) |
84 | { | 245 | { |
85 | mutex_lock(&hdmi.hdmi_lock); | 246 | mutex_lock(&hdmi.lock); |
86 | 247 | ||
87 | 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); | ||
88 | omapdss_hdmi_display_disable(dssdev); | 255 | omapdss_hdmi_display_disable(dssdev); |
256 | } | ||
89 | 257 | ||
90 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | 258 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
91 | 259 | ||
92 | mutex_unlock(&hdmi.hdmi_lock); | 260 | mutex_unlock(&hdmi.lock); |
93 | } | 261 | } |
94 | 262 | ||
95 | static int hdmi_panel_suspend(struct omap_dss_device *dssdev) | 263 | static int hdmi_panel_suspend(struct omap_dss_device *dssdev) |
96 | { | 264 | { |
97 | int r = 0; | 265 | int r = 0; |
98 | 266 | ||
99 | mutex_lock(&hdmi.hdmi_lock); | 267 | mutex_lock(&hdmi.lock); |
100 | 268 | ||
101 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { | 269 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { |
102 | r = -EINVAL; | 270 | r = -EINVAL; |
103 | goto err; | 271 | goto err; |
104 | } | 272 | } |
105 | 273 | ||
106 | 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); | ||
107 | 279 | ||
280 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | ||
108 | omapdss_hdmi_display_disable(dssdev); | 281 | omapdss_hdmi_display_disable(dssdev); |
109 | 282 | ||
110 | err: | 283 | err: |
111 | mutex_unlock(&hdmi.hdmi_lock); | 284 | mutex_unlock(&hdmi.lock); |
112 | 285 | ||
113 | return r; | 286 | return r; |
114 | } | 287 | } |
@@ -117,7 +290,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) | |||
117 | { | 290 | { |
118 | int r = 0; | 291 | int r = 0; |
119 | 292 | ||
120 | mutex_lock(&hdmi.hdmi_lock); | 293 | mutex_lock(&hdmi.lock); |
121 | 294 | ||
122 | if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { | 295 | if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { |
123 | r = -EINVAL; | 296 | r = -EINVAL; |
@@ -129,11 +302,12 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) | |||
129 | DSSERR("failed to power on\n"); | 302 | DSSERR("failed to power on\n"); |
130 | goto err; | 303 | goto err; |
131 | } | 304 | } |
305 | /* TODO: notify audio users that the panel resumed. */ | ||
132 | 306 | ||
133 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | 307 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
134 | 308 | ||
135 | err: | 309 | err: |
136 | mutex_unlock(&hdmi.hdmi_lock); | 310 | mutex_unlock(&hdmi.lock); |
137 | 311 | ||
138 | return r; | 312 | return r; |
139 | } | 313 | } |
@@ -141,11 +315,11 @@ err: | |||
141 | static void hdmi_get_timings(struct omap_dss_device *dssdev, | 315 | static void hdmi_get_timings(struct omap_dss_device *dssdev, |
142 | struct omap_video_timings *timings) | 316 | struct omap_video_timings *timings) |
143 | { | 317 | { |
144 | mutex_lock(&hdmi.hdmi_lock); | 318 | mutex_lock(&hdmi.lock); |
145 | 319 | ||
146 | *timings = dssdev->panel.timings; | 320 | *timings = dssdev->panel.timings; |
147 | 321 | ||
148 | mutex_unlock(&hdmi.hdmi_lock); | 322 | mutex_unlock(&hdmi.lock); |
149 | } | 323 | } |
150 | 324 | ||
151 | static void hdmi_set_timings(struct omap_dss_device *dssdev, | 325 | static void hdmi_set_timings(struct omap_dss_device *dssdev, |
@@ -153,12 +327,18 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, | |||
153 | { | 327 | { |
154 | DSSDBG("hdmi_set_timings\n"); | 328 | DSSDBG("hdmi_set_timings\n"); |
155 | 329 | ||
156 | mutex_lock(&hdmi.hdmi_lock); | 330 | mutex_lock(&hdmi.lock); |
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); | ||
157 | 337 | ||
158 | dssdev->panel.timings = *timings; | 338 | dssdev->panel.timings = *timings; |
159 | omapdss_hdmi_display_set_timing(dssdev); | 339 | omapdss_hdmi_display_set_timing(dssdev); |
160 | 340 | ||
161 | mutex_unlock(&hdmi.hdmi_lock); | 341 | mutex_unlock(&hdmi.lock); |
162 | } | 342 | } |
163 | 343 | ||
164 | static int hdmi_check_timings(struct omap_dss_device *dssdev, | 344 | static int hdmi_check_timings(struct omap_dss_device *dssdev, |
@@ -168,11 +348,11 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev, | |||
168 | 348 | ||
169 | DSSDBG("hdmi_check_timings\n"); | 349 | DSSDBG("hdmi_check_timings\n"); |
170 | 350 | ||
171 | mutex_lock(&hdmi.hdmi_lock); | 351 | mutex_lock(&hdmi.lock); |
172 | 352 | ||
173 | r = omapdss_hdmi_display_check_timing(dssdev, timings); | 353 | r = omapdss_hdmi_display_check_timing(dssdev, timings); |
174 | 354 | ||
175 | mutex_unlock(&hdmi.hdmi_lock); | 355 | mutex_unlock(&hdmi.lock); |
176 | return r; | 356 | return r; |
177 | } | 357 | } |
178 | 358 | ||
@@ -180,7 +360,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) | |||
180 | { | 360 | { |
181 | int r; | 361 | int r; |
182 | 362 | ||
183 | mutex_lock(&hdmi.hdmi_lock); | 363 | mutex_lock(&hdmi.lock); |
184 | 364 | ||
185 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { | 365 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { |
186 | r = omapdss_hdmi_display_enable(dssdev); | 366 | r = omapdss_hdmi_display_enable(dssdev); |
@@ -194,7 +374,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) | |||
194 | dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) | 374 | dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) |
195 | omapdss_hdmi_display_disable(dssdev); | 375 | omapdss_hdmi_display_disable(dssdev); |
196 | err: | 376 | err: |
197 | mutex_unlock(&hdmi.hdmi_lock); | 377 | mutex_unlock(&hdmi.lock); |
198 | 378 | ||
199 | return r; | 379 | return r; |
200 | } | 380 | } |
@@ -203,7 +383,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) | |||
203 | { | 383 | { |
204 | int r; | 384 | int r; |
205 | 385 | ||
206 | mutex_lock(&hdmi.hdmi_lock); | 386 | mutex_lock(&hdmi.lock); |
207 | 387 | ||
208 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { | 388 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { |
209 | r = omapdss_hdmi_display_enable(dssdev); | 389 | r = omapdss_hdmi_display_enable(dssdev); |
@@ -217,7 +397,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) | |||
217 | dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) | 397 | dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) |
218 | omapdss_hdmi_display_disable(dssdev); | 398 | omapdss_hdmi_display_disable(dssdev); |
219 | err: | 399 | err: |
220 | mutex_unlock(&hdmi.hdmi_lock); | 400 | mutex_unlock(&hdmi.lock); |
221 | 401 | ||
222 | return r; | 402 | return r; |
223 | } | 403 | } |
@@ -234,6 +414,12 @@ static struct omap_dss_driver hdmi_driver = { | |||
234 | .check_timings = hdmi_check_timings, | 414 | .check_timings = hdmi_check_timings, |
235 | .read_edid = hdmi_read_edid, | 415 | .read_edid = hdmi_read_edid, |
236 | .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, | ||
237 | .driver = { | 423 | .driver = { |
238 | .name = "hdmi_panel", | 424 | .name = "hdmi_panel", |
239 | .owner = THIS_MODULE, | 425 | .owner = THIS_MODULE, |
@@ -242,7 +428,11 @@ static struct omap_dss_driver hdmi_driver = { | |||
242 | 428 | ||
243 | int hdmi_panel_init(void) | 429 | int hdmi_panel_init(void) |
244 | { | 430 | { |
245 | mutex_init(&hdmi.hdmi_lock); | 431 | mutex_init(&hdmi.lock); |
432 | |||
433 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||
434 | spin_lock_init(&hdmi.audio_lock); | ||
435 | #endif | ||
246 | 436 | ||
247 | omap_dss_register_driver(&hdmi_driver); | 437 | omap_dss_register_driver(&hdmi_driver); |
248 | 438 | ||