Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hdmi_panel.c
Go to the documentation of this file.
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 <[email protected]>
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)
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 
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)
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();
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)
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);
276  }
277 
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 
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 ||
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 ||
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 
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 }