blob: 2feb2cdfb60fac7e36481615cfe81f4b10178a93 [file] [log] [blame]
Mythri P K70be8322011-03-10 15:48:48 +05301/*
Mythri P Kd875f992011-09-08 19:06:27 +05302 * hdmi_panel.c
Mythri P K70be8322011-03-10 15:48:48 +05303 *
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>
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030027#include <video/omapdss.h>
Tomi Valkeinen759593f2011-08-29 18:10:20 +030028#include <linux/slab.h>
Mythri P K70be8322011-03-10 15:48:48 +053029
30#include "dss.h"
31
32static struct {
Ricardo Nerib7dea05a2012-04-25 19:29:06 -050033 /* This protects the panel ops, mainly when accessing the HDMI IP. */
34 struct mutex lock;
Ricardo Nerif3a974912012-05-09 21:09:50 -050035#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
36 /* This protects the audio ops, specifically. */
37 spinlock_t audio_lock;
38#endif
Mythri P K70be8322011-03-10 15:48:48 +053039} hdmi;
40
41
42static int hdmi_panel_probe(struct omap_dss_device *dssdev)
43{
Archit Taneja78493982012-08-08 16:50:42 +053044 /* 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
Mythri P K70be8322011-03-10 15:48:48 +053062 DSSDBG("ENTER hdmi_panel_probe\n");
63
Archit Taneja78493982012-08-08 16:50:42 +053064 dssdev->panel.timings = default_timings;
Mythri P K70be8322011-03-10 15:48:48 +053065
66 DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
67 dssdev->panel.timings.x_res,
68 dssdev->panel.timings.y_res);
Archit Taneja78493982012-08-08 16:50:42 +053069
Mythri P K70be8322011-03-10 15:48:48 +053070 return 0;
71}
72
73static void hdmi_panel_remove(struct omap_dss_device *dssdev)
74{
75
76}
77
Ricardo Nerif3a974912012-05-09 21:09:50 -050078#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
79static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
80{
81 unsigned long flags;
82 int r;
83
84 mutex_lock(&hdmi.lock);
85 spin_lock_irqsave(&hdmi.audio_lock, flags);
86
87 /* enable audio only if the display is active and supports audio */
88 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
89 !hdmi_mode_has_audio()) {
90 DSSERR("audio not supported or display is off\n");
91 r = -EPERM;
92 goto err;
93 }
94
95 r = hdmi_audio_enable();
96
97 if (!r)
98 dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
99
100err:
101 spin_unlock_irqrestore(&hdmi.audio_lock, flags);
102 mutex_unlock(&hdmi.lock);
103 return r;
104}
105
106static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
107{
108 unsigned long flags;
109
110 spin_lock_irqsave(&hdmi.audio_lock, flags);
111
112 hdmi_audio_disable();
113
114 dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
115
116 spin_unlock_irqrestore(&hdmi.audio_lock, flags);
117}
118
119static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
120{
121 unsigned long flags;
122 int r;
123
124 spin_lock_irqsave(&hdmi.audio_lock, flags);
125 /*
126 * No need to check the panel state. It was checked when trasitioning
127 * to AUDIO_ENABLED.
128 */
129 if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) {
130 DSSERR("audio start from invalid state\n");
131 r = -EPERM;
132 goto err;
133 }
134
135 r = hdmi_audio_start();
136
137 if (!r)
138 dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
139
140err:
141 spin_unlock_irqrestore(&hdmi.audio_lock, flags);
142 return r;
143}
144
145static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
146{
147 unsigned long flags;
148
149 spin_lock_irqsave(&hdmi.audio_lock, flags);
150
151 hdmi_audio_stop();
152 dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
153
154 spin_unlock_irqrestore(&hdmi.audio_lock, flags);
155}
156
157static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
158{
159 bool r = false;
160
161 mutex_lock(&hdmi.lock);
162
163 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
164 goto err;
165
166 if (!hdmi_mode_has_audio())
167 goto err;
168
169 r = true;
170err:
171 mutex_unlock(&hdmi.lock);
172 return r;
173}
174
175static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
176 struct omap_dss_audio *audio)
177{
178 unsigned long flags;
179 int r;
180
181 mutex_lock(&hdmi.lock);
182 spin_lock_irqsave(&hdmi.audio_lock, flags);
183
184 /* config audio only if the display is active and supports audio */
185 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
186 !hdmi_mode_has_audio()) {
187 DSSERR("audio not supported or display is off\n");
188 r = -EPERM;
189 goto err;
190 }
191
192 r = hdmi_audio_config(audio);
193
194 if (!r)
195 dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
196
197err:
198 spin_unlock_irqrestore(&hdmi.audio_lock, flags);
199 mutex_unlock(&hdmi.lock);
200 return r;
201}
202
203#else
204static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
205{
206 return -EPERM;
207}
208
209static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
210{
211}
212
213static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
214{
215 return -EPERM;
216}
217
218static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
219{
220}
221
222static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
223{
224 return false;
225}
226
227static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
228 struct omap_dss_audio *audio)
229{
230 return -EPERM;
231}
232#endif
233
Mythri P K70be8322011-03-10 15:48:48 +0530234static int hdmi_panel_enable(struct omap_dss_device *dssdev)
235{
236 int r = 0;
237 DSSDBG("ENTER hdmi_panel_enable\n");
238
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500239 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530240
241 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
242 r = -EINVAL;
243 goto err;
244 }
245
Archit Taneja78493982012-08-08 16:50:42 +0530246 omapdss_hdmi_display_set_timing(dssdev, &dssdev->panel.timings);
247
Mythri P K70be8322011-03-10 15:48:48 +0530248 r = omapdss_hdmi_display_enable(dssdev);
249 if (r) {
250 DSSERR("failed to power on\n");
251 goto err;
252 }
253
254 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
255
256err:
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500257 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530258
259 return r;
260}
261
262static void hdmi_panel_disable(struct omap_dss_device *dssdev)
263{
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500264 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530265
Ricardo Nerif3a974912012-05-09 21:09:50 -0500266 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
267 /*
268 * TODO: notify audio users that the display was disabled. For
269 * now, disable audio locally to not break our audio state
270 * machine.
271 */
272 hdmi_panel_audio_disable(dssdev);
Mythri P K70be8322011-03-10 15:48:48 +0530273 omapdss_hdmi_display_disable(dssdev);
Ricardo Nerif3a974912012-05-09 21:09:50 -0500274 }
Mythri P K70be8322011-03-10 15:48:48 +0530275
276 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
277
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500278 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530279}
280
281static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
282{
283 int r = 0;
284
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500285 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530286
287 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
288 r = -EINVAL;
289 goto err;
290 }
291
Ricardo Nerif3a974912012-05-09 21:09:50 -0500292 /*
293 * TODO: notify audio users that the display was suspended. For now,
294 * disable audio locally to not break our audio state machine.
295 */
296 hdmi_panel_audio_disable(dssdev);
Mythri P K70be8322011-03-10 15:48:48 +0530297
Ricardo Nerif3a974912012-05-09 21:09:50 -0500298 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
Mythri P K70be8322011-03-10 15:48:48 +0530299 omapdss_hdmi_display_disable(dssdev);
300
301err:
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500302 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530303
304 return r;
305}
306
307static int hdmi_panel_resume(struct omap_dss_device *dssdev)
308{
309 int r = 0;
310
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500311 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530312
313 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
314 r = -EINVAL;
315 goto err;
316 }
317
318 r = omapdss_hdmi_display_enable(dssdev);
319 if (r) {
320 DSSERR("failed to power on\n");
321 goto err;
322 }
Ricardo Nerif3a974912012-05-09 21:09:50 -0500323 /* TODO: notify audio users that the panel resumed. */
Mythri P K70be8322011-03-10 15:48:48 +0530324
325 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
326
327err:
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500328 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530329
330 return r;
331}
332
333static void hdmi_get_timings(struct omap_dss_device *dssdev,
334 struct omap_video_timings *timings)
335{
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500336 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530337
338 *timings = dssdev->panel.timings;
339
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500340 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530341}
342
343static void hdmi_set_timings(struct omap_dss_device *dssdev,
344 struct omap_video_timings *timings)
345{
346 DSSDBG("hdmi_set_timings\n");
347
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500348 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530349
Ricardo Nerif3a974912012-05-09 21:09:50 -0500350 /*
351 * TODO: notify audio users that there was a timings change. For
352 * now, disable audio locally to not break our audio state machine.
353 */
354 hdmi_panel_audio_disable(dssdev);
355
Archit Taneja78493982012-08-08 16:50:42 +0530356 omapdss_hdmi_display_set_timing(dssdev, timings);
Mythri P K70be8322011-03-10 15:48:48 +0530357 dssdev->panel.timings = *timings;
Mythri P K70be8322011-03-10 15:48:48 +0530358
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500359 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530360}
361
362static int hdmi_check_timings(struct omap_dss_device *dssdev,
363 struct omap_video_timings *timings)
364{
365 int r = 0;
366
367 DSSDBG("hdmi_check_timings\n");
368
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500369 mutex_lock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530370
371 r = omapdss_hdmi_display_check_timing(dssdev, timings);
Tomi Valkeinen468c1b92011-08-25 17:13:32 +0300372
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500373 mutex_unlock(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530374 return r;
375}
376
Tomi Valkeinen47024562011-08-25 17:12:56 +0300377static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
378{
379 int r;
380
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500381 mutex_lock(&hdmi.lock);
Tomi Valkeinen47024562011-08-25 17:12:56 +0300382
383 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
384 r = omapdss_hdmi_display_enable(dssdev);
385 if (r)
386 goto err;
387 }
388
389 r = omapdss_hdmi_read_edid(buf, len);
390
391 if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
392 dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
393 omapdss_hdmi_display_disable(dssdev);
394err:
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500395 mutex_unlock(&hdmi.lock);
Tomi Valkeinen47024562011-08-25 17:12:56 +0300396
397 return r;
398}
399
Tomi Valkeinen759593f2011-08-29 18:10:20 +0300400static bool hdmi_detect(struct omap_dss_device *dssdev)
401{
402 int r;
403
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500404 mutex_lock(&hdmi.lock);
Tomi Valkeinen759593f2011-08-29 18:10:20 +0300405
406 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
407 r = omapdss_hdmi_display_enable(dssdev);
408 if (r)
409 goto err;
410 }
411
412 r = omapdss_hdmi_detect();
413
414 if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
415 dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
416 omapdss_hdmi_display_disable(dssdev);
417err:
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500418 mutex_unlock(&hdmi.lock);
Tomi Valkeinen759593f2011-08-29 18:10:20 +0300419
420 return r;
421}
422
Mythri P K70be8322011-03-10 15:48:48 +0530423static struct omap_dss_driver hdmi_driver = {
424 .probe = hdmi_panel_probe,
425 .remove = hdmi_panel_remove,
426 .enable = hdmi_panel_enable,
427 .disable = hdmi_panel_disable,
428 .suspend = hdmi_panel_suspend,
429 .resume = hdmi_panel_resume,
430 .get_timings = hdmi_get_timings,
431 .set_timings = hdmi_set_timings,
432 .check_timings = hdmi_check_timings,
Tomi Valkeinen47024562011-08-25 17:12:56 +0300433 .read_edid = hdmi_read_edid,
Tomi Valkeinen759593f2011-08-29 18:10:20 +0300434 .detect = hdmi_detect,
Ricardo Nerif3a974912012-05-09 21:09:50 -0500435 .audio_enable = hdmi_panel_audio_enable,
436 .audio_disable = hdmi_panel_audio_disable,
437 .audio_start = hdmi_panel_audio_start,
438 .audio_stop = hdmi_panel_audio_stop,
439 .audio_supported = hdmi_panel_audio_supported,
440 .audio_config = hdmi_panel_audio_config,
Mythri P K70be8322011-03-10 15:48:48 +0530441 .driver = {
442 .name = "hdmi_panel",
443 .owner = THIS_MODULE,
444 },
445};
446
447int hdmi_panel_init(void)
448{
Ricardo Nerib7dea05a2012-04-25 19:29:06 -0500449 mutex_init(&hdmi.lock);
Mythri P K70be8322011-03-10 15:48:48 +0530450
Ricardo Nerif3a974912012-05-09 21:09:50 -0500451#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
452 spin_lock_init(&hdmi.audio_lock);
453#endif
454
Mythri P K70be8322011-03-10 15:48:48 +0530455 omap_dss_register_driver(&hdmi_driver);
456
457 return 0;
458}
459
460void hdmi_panel_exit(void)
461{
462 omap_dss_unregister_driver(&hdmi_driver);
463
464}