Commit | Line | Data |
---|---|---|
c3198a5e | 1 | /* |
ef26958a | 2 | * HDMI interface DSS driver for TI's OMAP4 family of SoCs. |
c3198a5e M |
3 | * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ |
4 | * Authors: Yong Zhi | |
5 | * Mythri pk <mythripk@ti.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #define DSS_SUBSYS_NAME "HDMI" | |
21 | ||
22 | #include <linux/kernel.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/err.h> | |
25 | #include <linux/io.h> | |
26 | #include <linux/interrupt.h> | |
27 | #include <linux/mutex.h> | |
28 | #include <linux/delay.h> | |
29 | #include <linux/string.h> | |
24e6289c | 30 | #include <linux/platform_device.h> |
4fbafaf3 TV |
31 | #include <linux/pm_runtime.h> |
32 | #include <linux/clk.h> | |
cca35017 | 33 | #include <linux/gpio.h> |
17486943 | 34 | #include <linux/regulator/consumer.h> |
a0b38cc4 | 35 | #include <video/omapdss.h> |
c3198a5e | 36 | |
ef26958a | 37 | #include "hdmi4_core.h" |
c3198a5e | 38 | #include "dss.h" |
ad44cc32 | 39 | #include "dss_features.h" |
c3198a5e M |
40 | |
41 | static struct { | |
42 | struct mutex lock; | |
c3198a5e | 43 | struct platform_device *pdev; |
66a06b0c | 44 | |
275cfa1a AT |
45 | struct hdmi_wp_data wp; |
46 | struct hdmi_pll_data pll; | |
47 | struct hdmi_phy_data phy; | |
48 | struct hdmi_core_data core; | |
49 | ||
50 | struct hdmi_config cfg; | |
4fbafaf3 TV |
51 | |
52 | struct clk *sys_clk; | |
17486943 | 53 | struct regulator *vdda_hdmi_dac_reg; |
cca35017 | 54 | |
0b450c31 TV |
55 | bool core_enabled; |
56 | ||
1f68d9c4 | 57 | struct omap_dss_device output; |
c3198a5e M |
58 | } hdmi; |
59 | ||
4fbafaf3 TV |
60 | static int hdmi_runtime_get(void) |
61 | { | |
62 | int r; | |
63 | ||
64 | DSSDBG("hdmi_runtime_get\n"); | |
65 | ||
66 | r = pm_runtime_get_sync(&hdmi.pdev->dev); | |
67 | WARN_ON(r < 0); | |
a247ce78 | 68 | if (r < 0) |
852f0838 | 69 | return r; |
a247ce78 AT |
70 | |
71 | return 0; | |
4fbafaf3 TV |
72 | } |
73 | ||
74 | static void hdmi_runtime_put(void) | |
75 | { | |
76 | int r; | |
77 | ||
78 | DSSDBG("hdmi_runtime_put\n"); | |
79 | ||
0eaf9f52 | 80 | r = pm_runtime_put_sync(&hdmi.pdev->dev); |
5be3aebd | 81 | WARN_ON(r < 0 && r != -ENOSYS); |
4fbafaf3 TV |
82 | } |
83 | ||
dcf5f729 TV |
84 | static irqreturn_t hdmi_irq_handler(int irq, void *data) |
85 | { | |
86 | struct hdmi_wp_data *wp = data; | |
87 | u32 irqstatus; | |
88 | ||
89 | irqstatus = hdmi_wp_get_irqstatus(wp); | |
90 | hdmi_wp_set_irqstatus(wp, irqstatus); | |
91 | ||
92 | if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && | |
93 | irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | |
94 | /* | |
95 | * If we get both connect and disconnect interrupts at the same | |
96 | * time, turn off the PHY, clear interrupts, and restart, which | |
97 | * raises connect interrupt if a cable is connected, or nothing | |
98 | * if cable is not connected. | |
99 | */ | |
100 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); | |
101 | ||
102 | hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | | |
103 | HDMI_IRQ_LINK_DISCONNECT); | |
104 | ||
105 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | |
106 | } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { | |
107 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); | |
108 | } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | |
109 | hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | |
110 | } | |
111 | ||
112 | return IRQ_HANDLED; | |
113 | } | |
114 | ||
e25001d8 TV |
115 | static int hdmi_init_regulator(void) |
116 | { | |
818a053c | 117 | int r; |
e25001d8 TV |
118 | struct regulator *reg; |
119 | ||
120 | if (hdmi.vdda_hdmi_dac_reg != NULL) | |
121 | return 0; | |
122 | ||
931d4bd6 | 123 | reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); |
e25001d8 TV |
124 | |
125 | if (IS_ERR(reg)) { | |
40359a9b | 126 | if (PTR_ERR(reg) != -EPROBE_DEFER) |
931d4bd6 | 127 | DSSERR("can't get VDDA regulator\n"); |
e25001d8 TV |
128 | return PTR_ERR(reg); |
129 | } | |
130 | ||
818a053c TV |
131 | if (regulator_can_change_voltage(reg)) { |
132 | r = regulator_set_voltage(reg, 1800000, 1800000); | |
133 | if (r) { | |
134 | devm_regulator_put(reg); | |
135 | DSSWARN("can't set the regulator voltage\n"); | |
136 | return r; | |
137 | } | |
138 | } | |
139 | ||
e25001d8 TV |
140 | hdmi.vdda_hdmi_dac_reg = reg; |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
bb426fc9 | 145 | static int hdmi_power_on_core(struct omap_dss_device *dssdev) |
c3198a5e | 146 | { |
46095b2d | 147 | int r; |
c3198a5e | 148 | |
17486943 TV |
149 | r = regulator_enable(hdmi.vdda_hdmi_dac_reg); |
150 | if (r) | |
164ebdd1 | 151 | return r; |
17486943 | 152 | |
4fbafaf3 TV |
153 | r = hdmi_runtime_get(); |
154 | if (r) | |
cca35017 | 155 | goto err_runtime_get; |
c3198a5e | 156 | |
bb426fc9 TV |
157 | /* Make selection of HDMI in DSS */ |
158 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | |
159 | ||
0b450c31 TV |
160 | hdmi.core_enabled = true; |
161 | ||
bb426fc9 TV |
162 | return 0; |
163 | ||
164 | err_runtime_get: | |
165 | regulator_disable(hdmi.vdda_hdmi_dac_reg); | |
164ebdd1 | 166 | |
bb426fc9 TV |
167 | return r; |
168 | } | |
169 | ||
170 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) | |
171 | { | |
0b450c31 TV |
172 | hdmi.core_enabled = false; |
173 | ||
bb426fc9 TV |
174 | hdmi_runtime_put(); |
175 | regulator_disable(hdmi.vdda_hdmi_dac_reg); | |
bb426fc9 TV |
176 | } |
177 | ||
178 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |
179 | { | |
180 | int r; | |
181 | struct omap_video_timings *p; | |
7ae9a71e | 182 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
bb426fc9 | 183 | unsigned long phy; |
dcf5f729 | 184 | struct hdmi_wp_data *wp = &hdmi.wp; |
bb426fc9 TV |
185 | |
186 | r = hdmi_power_on_core(dssdev); | |
187 | if (r) | |
188 | return r; | |
189 | ||
dcf5f729 TV |
190 | /* disable and clear irqs */ |
191 | hdmi_wp_clear_irqenable(wp, 0xffffffff); | |
192 | hdmi_wp_set_irqstatus(wp, 0xffffffff); | |
193 | ||
275cfa1a | 194 | p = &hdmi.cfg.timings; |
c3198a5e | 195 | |
7849398f | 196 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); |
c3198a5e | 197 | |
d8d78941 TV |
198 | /* the functions below use kHz pixel clock. TODO: change to Hz */ |
199 | phy = p->pixelclock / 1000; | |
c3198a5e | 200 | |
275cfa1a | 201 | hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); |
c3198a5e | 202 | |
95a8aeb6 | 203 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ |
275cfa1a | 204 | r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); |
c3198a5e M |
205 | if (r) { |
206 | DSSDBG("Failed to lock PLL\n"); | |
cca35017 | 207 | goto err_pll_enable; |
c3198a5e M |
208 | } |
209 | ||
dcf5f729 | 210 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); |
c3198a5e | 211 | if (r) { |
dcf5f729 TV |
212 | DSSDBG("Failed to configure PHY\n"); |
213 | goto err_phy_cfg; | |
c3198a5e M |
214 | } |
215 | ||
dcf5f729 TV |
216 | r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); |
217 | if (r) | |
218 | goto err_phy_pwr; | |
219 | ||
275cfa1a | 220 | hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); |
c3198a5e | 221 | |
c3198a5e M |
222 | /* bypass TV gamma table */ |
223 | dispc_enable_gamma_table(0); | |
224 | ||
225 | /* tv size */ | |
cea87b92 | 226 | dss_mgr_set_timings(mgr, p); |
c3198a5e | 227 | |
275cfa1a | 228 | r = hdmi_wp_video_start(&hdmi.wp); |
c0456be3 RN |
229 | if (r) |
230 | goto err_vid_enable; | |
c3198a5e | 231 | |
cea87b92 | 232 | r = dss_mgr_enable(mgr); |
33ca237f TV |
233 | if (r) |
234 | goto err_mgr_enable; | |
3870c909 | 235 | |
dcf5f729 TV |
236 | hdmi_wp_set_irqenable(wp, |
237 | HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); | |
238 | ||
c3198a5e | 239 | return 0; |
33ca237f TV |
240 | |
241 | err_mgr_enable: | |
275cfa1a | 242 | hdmi_wp_video_stop(&hdmi.wp); |
c0456be3 | 243 | err_vid_enable: |
dcf5f729 TV |
244 | err_phy_cfg: |
245 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | |
246 | err_phy_pwr: | |
275cfa1a | 247 | hdmi_pll_disable(&hdmi.pll, &hdmi.wp); |
cca35017 | 248 | err_pll_enable: |
bb426fc9 | 249 | hdmi_power_off_core(dssdev); |
c3198a5e M |
250 | return -EIO; |
251 | } | |
252 | ||
bb426fc9 | 253 | static void hdmi_power_off_full(struct omap_dss_device *dssdev) |
c3198a5e | 254 | { |
7ae9a71e | 255 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
cea87b92 | 256 | |
dcf5f729 TV |
257 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); |
258 | ||
cea87b92 | 259 | dss_mgr_disable(mgr); |
c3198a5e | 260 | |
275cfa1a | 261 | hdmi_wp_video_stop(&hdmi.wp); |
dcf5f729 TV |
262 | |
263 | hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | |
264 | ||
275cfa1a | 265 | hdmi_pll_disable(&hdmi.pll, &hdmi.wp); |
17486943 | 266 | |
bb426fc9 | 267 | hdmi_power_off_core(dssdev); |
c3198a5e M |
268 | } |
269 | ||
164ebdd1 | 270 | static int hdmi_display_check_timing(struct omap_dss_device *dssdev, |
c3198a5e M |
271 | struct omap_video_timings *timings) |
272 | { | |
1e676248 | 273 | struct omap_dss_device *out = &hdmi.output; |
c3198a5e | 274 | |
1e676248 | 275 | if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) |
c3198a5e | 276 | return -EINVAL; |
c3198a5e M |
277 | |
278 | return 0; | |
c3198a5e M |
279 | } |
280 | ||
164ebdd1 | 281 | static void hdmi_display_set_timing(struct omap_dss_device *dssdev, |
7849398f | 282 | struct omap_video_timings *timings) |
c3198a5e | 283 | { |
ed1aa900 AT |
284 | mutex_lock(&hdmi.lock); |
285 | ||
ab0aee95 | 286 | hdmi.cfg.timings = *timings; |
5391e87d | 287 | |
ab0aee95 | 288 | dispc_set_tv_pclk(timings->pixelclock); |
1e676248 | 289 | |
ed1aa900 | 290 | mutex_unlock(&hdmi.lock); |
c3198a5e M |
291 | } |
292 | ||
164ebdd1 | 293 | static void hdmi_display_get_timings(struct omap_dss_device *dssdev, |
0b450c31 TV |
294 | struct omap_video_timings *timings) |
295 | { | |
ab0aee95 | 296 | *timings = hdmi.cfg.timings; |
0b450c31 TV |
297 | } |
298 | ||
e40402cf | 299 | static void hdmi_dump_regs(struct seq_file *s) |
162874d5 M |
300 | { |
301 | mutex_lock(&hdmi.lock); | |
302 | ||
f8fb7d7b WY |
303 | if (hdmi_runtime_get()) { |
304 | mutex_unlock(&hdmi.lock); | |
162874d5 | 305 | return; |
f8fb7d7b | 306 | } |
162874d5 | 307 | |
275cfa1a AT |
308 | hdmi_wp_dump(&hdmi.wp, s); |
309 | hdmi_pll_dump(&hdmi.pll, s); | |
310 | hdmi_phy_dump(&hdmi.phy, s); | |
311 | hdmi4_core_dump(&hdmi.core, s); | |
162874d5 M |
312 | |
313 | hdmi_runtime_put(); | |
314 | mutex_unlock(&hdmi.lock); | |
315 | } | |
316 | ||
164ebdd1 | 317 | static int read_edid(u8 *buf, int len) |
47024565 TV |
318 | { |
319 | int r; | |
320 | ||
321 | mutex_lock(&hdmi.lock); | |
322 | ||
323 | r = hdmi_runtime_get(); | |
324 | BUG_ON(r); | |
325 | ||
275cfa1a | 326 | r = hdmi4_read_edid(&hdmi.core, buf, len); |
47024565 TV |
327 | |
328 | hdmi_runtime_put(); | |
329 | mutex_unlock(&hdmi.lock); | |
330 | ||
331 | return r; | |
332 | } | |
333 | ||
164ebdd1 | 334 | static int hdmi_display_enable(struct omap_dss_device *dssdev) |
c3198a5e | 335 | { |
1f68d9c4 | 336 | struct omap_dss_device *out = &hdmi.output; |
c3198a5e M |
337 | int r = 0; |
338 | ||
339 | DSSDBG("ENTER hdmi_display_enable\n"); | |
340 | ||
341 | mutex_lock(&hdmi.lock); | |
342 | ||
cea87b92 AT |
343 | if (out == NULL || out->manager == NULL) { |
344 | DSSERR("failed to enable display: no output/manager\n"); | |
05e1d606 TV |
345 | r = -ENODEV; |
346 | goto err0; | |
347 | } | |
348 | ||
bb426fc9 | 349 | r = hdmi_power_on_full(dssdev); |
c3198a5e M |
350 | if (r) { |
351 | DSSERR("failed to power on device\n"); | |
d3923933 | 352 | goto err0; |
c3198a5e M |
353 | } |
354 | ||
355 | mutex_unlock(&hdmi.lock); | |
356 | return 0; | |
357 | ||
c3198a5e M |
358 | err0: |
359 | mutex_unlock(&hdmi.lock); | |
360 | return r; | |
361 | } | |
362 | ||
164ebdd1 | 363 | static void hdmi_display_disable(struct omap_dss_device *dssdev) |
c3198a5e M |
364 | { |
365 | DSSDBG("Enter hdmi_display_disable\n"); | |
366 | ||
367 | mutex_lock(&hdmi.lock); | |
368 | ||
bb426fc9 | 369 | hdmi_power_off_full(dssdev); |
c3198a5e | 370 | |
c3198a5e M |
371 | mutex_unlock(&hdmi.lock); |
372 | } | |
373 | ||
164ebdd1 | 374 | static int hdmi_core_enable(struct omap_dss_device *dssdev) |
4489823c TV |
375 | { |
376 | int r = 0; | |
377 | ||
378 | DSSDBG("ENTER omapdss_hdmi_core_enable\n"); | |
379 | ||
380 | mutex_lock(&hdmi.lock); | |
381 | ||
4489823c TV |
382 | r = hdmi_power_on_core(dssdev); |
383 | if (r) { | |
384 | DSSERR("failed to power on device\n"); | |
385 | goto err0; | |
386 | } | |
387 | ||
388 | mutex_unlock(&hdmi.lock); | |
389 | return 0; | |
390 | ||
391 | err0: | |
392 | mutex_unlock(&hdmi.lock); | |
393 | return r; | |
394 | } | |
395 | ||
164ebdd1 | 396 | static void hdmi_core_disable(struct omap_dss_device *dssdev) |
4489823c TV |
397 | { |
398 | DSSDBG("Enter omapdss_hdmi_core_disable\n"); | |
399 | ||
400 | mutex_lock(&hdmi.lock); | |
401 | ||
402 | hdmi_power_off_core(dssdev); | |
403 | ||
404 | mutex_unlock(&hdmi.lock); | |
405 | } | |
406 | ||
4fbafaf3 TV |
407 | static int hdmi_get_clocks(struct platform_device *pdev) |
408 | { | |
409 | struct clk *clk; | |
410 | ||
b2c9c8ee | 411 | clk = devm_clk_get(&pdev->dev, "sys_clk"); |
4fbafaf3 TV |
412 | if (IS_ERR(clk)) { |
413 | DSSERR("can't get sys_clk\n"); | |
414 | return PTR_ERR(clk); | |
415 | } | |
416 | ||
417 | hdmi.sys_clk = clk; | |
418 | ||
4fbafaf3 TV |
419 | return 0; |
420 | } | |
421 | ||
0b450c31 TV |
422 | static int hdmi_connect(struct omap_dss_device *dssdev, |
423 | struct omap_dss_device *dst) | |
424 | { | |
425 | struct omap_overlay_manager *mgr; | |
426 | int r; | |
427 | ||
0b450c31 TV |
428 | r = hdmi_init_regulator(); |
429 | if (r) | |
430 | return r; | |
431 | ||
432 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | |
433 | if (!mgr) | |
434 | return -ENODEV; | |
435 | ||
436 | r = dss_mgr_connect(mgr, dssdev); | |
437 | if (r) | |
438 | return r; | |
439 | ||
440 | r = omapdss_output_set_device(dssdev, dst); | |
441 | if (r) { | |
442 | DSSERR("failed to connect output to new device: %s\n", | |
443 | dst->name); | |
444 | dss_mgr_disconnect(mgr, dssdev); | |
445 | return r; | |
446 | } | |
447 | ||
448 | return 0; | |
449 | } | |
450 | ||
451 | static void hdmi_disconnect(struct omap_dss_device *dssdev, | |
452 | struct omap_dss_device *dst) | |
453 | { | |
9560dc10 | 454 | WARN_ON(dst != dssdev->dst); |
0b450c31 | 455 | |
9560dc10 | 456 | if (dst != dssdev->dst) |
0b450c31 TV |
457 | return; |
458 | ||
459 | omapdss_output_unset_device(dssdev); | |
460 | ||
461 | if (dssdev->manager) | |
462 | dss_mgr_disconnect(dssdev->manager, dssdev); | |
463 | } | |
464 | ||
465 | static int hdmi_read_edid(struct omap_dss_device *dssdev, | |
466 | u8 *edid, int len) | |
467 | { | |
468 | bool need_enable; | |
469 | int r; | |
470 | ||
471 | need_enable = hdmi.core_enabled == false; | |
472 | ||
473 | if (need_enable) { | |
164ebdd1 | 474 | r = hdmi_core_enable(dssdev); |
0b450c31 TV |
475 | if (r) |
476 | return r; | |
477 | } | |
478 | ||
164ebdd1 | 479 | r = read_edid(edid, len); |
0b450c31 TV |
480 | |
481 | if (need_enable) | |
164ebdd1 | 482 | hdmi_core_disable(dssdev); |
0b450c31 TV |
483 | |
484 | return r; | |
485 | } | |
486 | ||
487 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | |
164ebdd1 | 488 | static int hdmi_audio_enable(struct omap_dss_device *dssdev) |
0b450c31 TV |
489 | { |
490 | int r; | |
491 | ||
492 | mutex_lock(&hdmi.lock); | |
493 | ||
ab0aee95 | 494 | if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { |
0b450c31 TV |
495 | r = -EPERM; |
496 | goto err; | |
497 | } | |
498 | ||
275cfa1a | 499 | r = hdmi_wp_audio_enable(&hdmi.wp, true); |
0b450c31 TV |
500 | if (r) |
501 | goto err; | |
502 | ||
503 | mutex_unlock(&hdmi.lock); | |
504 | return 0; | |
505 | ||
506 | err: | |
507 | mutex_unlock(&hdmi.lock); | |
508 | return r; | |
509 | } | |
510 | ||
164ebdd1 | 511 | static void hdmi_audio_disable(struct omap_dss_device *dssdev) |
0b450c31 | 512 | { |
275cfa1a | 513 | hdmi_wp_audio_enable(&hdmi.wp, false); |
0b450c31 TV |
514 | } |
515 | ||
164ebdd1 | 516 | static int hdmi_audio_start(struct omap_dss_device *dssdev) |
0b450c31 | 517 | { |
275cfa1a | 518 | return hdmi4_audio_start(&hdmi.core, &hdmi.wp); |
0b450c31 TV |
519 | } |
520 | ||
164ebdd1 | 521 | static void hdmi_audio_stop(struct omap_dss_device *dssdev) |
0b450c31 | 522 | { |
275cfa1a | 523 | hdmi4_audio_stop(&hdmi.core, &hdmi.wp); |
0b450c31 TV |
524 | } |
525 | ||
164ebdd1 | 526 | static bool hdmi_audio_supported(struct omap_dss_device *dssdev) |
0b450c31 TV |
527 | { |
528 | bool r; | |
529 | ||
530 | mutex_lock(&hdmi.lock); | |
531 | ||
ab0aee95 | 532 | r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode); |
0b450c31 TV |
533 | |
534 | mutex_unlock(&hdmi.lock); | |
535 | return r; | |
536 | } | |
537 | ||
164ebdd1 | 538 | static int hdmi_audio_config(struct omap_dss_device *dssdev, |
0b450c31 TV |
539 | struct omap_dss_audio *audio) |
540 | { | |
541 | int r; | |
d8d78941 | 542 | u32 pclk = hdmi.cfg.timings.pixelclock; |
0b450c31 TV |
543 | |
544 | mutex_lock(&hdmi.lock); | |
545 | ||
ab0aee95 | 546 | if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { |
0b450c31 TV |
547 | r = -EPERM; |
548 | goto err; | |
549 | } | |
550 | ||
08d83e4e | 551 | r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); |
0b450c31 TV |
552 | if (r) |
553 | goto err; | |
554 | ||
555 | mutex_unlock(&hdmi.lock); | |
556 | return 0; | |
557 | ||
558 | err: | |
559 | mutex_unlock(&hdmi.lock); | |
560 | return r; | |
561 | } | |
562 | #else | |
164ebdd1 | 563 | static int hdmi_audio_enable(struct omap_dss_device *dssdev) |
0b450c31 TV |
564 | { |
565 | return -EPERM; | |
566 | } | |
567 | ||
164ebdd1 | 568 | static void hdmi_audio_disable(struct omap_dss_device *dssdev) |
0b450c31 TV |
569 | { |
570 | } | |
571 | ||
164ebdd1 | 572 | static int hdmi_audio_start(struct omap_dss_device *dssdev) |
0b450c31 TV |
573 | { |
574 | return -EPERM; | |
575 | } | |
576 | ||
164ebdd1 | 577 | static void hdmi_audio_stop(struct omap_dss_device *dssdev) |
0b450c31 TV |
578 | { |
579 | } | |
580 | ||
164ebdd1 | 581 | static bool hdmi_audio_supported(struct omap_dss_device *dssdev) |
0b450c31 TV |
582 | { |
583 | return false; | |
584 | } | |
585 | ||
164ebdd1 | 586 | static int hdmi_audio_config(struct omap_dss_device *dssdev, |
0b450c31 TV |
587 | struct omap_dss_audio *audio) |
588 | { | |
589 | return -EPERM; | |
590 | } | |
591 | #endif | |
592 | ||
ab0aee95 TV |
593 | static int hdmi_set_infoframe(struct omap_dss_device *dssdev, |
594 | const struct hdmi_avi_infoframe *avi) | |
595 | { | |
596 | hdmi.cfg.infoframe = *avi; | |
597 | return 0; | |
598 | } | |
599 | ||
600 | static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, | |
601 | bool hdmi_mode) | |
602 | { | |
603 | hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; | |
604 | return 0; | |
605 | } | |
606 | ||
0b450c31 TV |
607 | static const struct omapdss_hdmi_ops hdmi_ops = { |
608 | .connect = hdmi_connect, | |
609 | .disconnect = hdmi_disconnect, | |
610 | ||
164ebdd1 TV |
611 | .enable = hdmi_display_enable, |
612 | .disable = hdmi_display_disable, | |
0b450c31 | 613 | |
164ebdd1 TV |
614 | .check_timings = hdmi_display_check_timing, |
615 | .set_timings = hdmi_display_set_timing, | |
616 | .get_timings = hdmi_display_get_timings, | |
0b450c31 TV |
617 | |
618 | .read_edid = hdmi_read_edid, | |
ab0aee95 TV |
619 | .set_infoframe = hdmi_set_infoframe, |
620 | .set_hdmi_mode = hdmi_set_hdmi_mode, | |
0b450c31 | 621 | |
164ebdd1 TV |
622 | .audio_enable = hdmi_audio_enable, |
623 | .audio_disable = hdmi_audio_disable, | |
624 | .audio_start = hdmi_audio_start, | |
625 | .audio_stop = hdmi_audio_stop, | |
626 | .audio_supported = hdmi_audio_supported, | |
627 | .audio_config = hdmi_audio_config, | |
0b450c31 TV |
628 | }; |
629 | ||
17ae4e8c | 630 | static void hdmi_init_output(struct platform_device *pdev) |
81b87f51 | 631 | { |
1f68d9c4 | 632 | struct omap_dss_device *out = &hdmi.output; |
81b87f51 | 633 | |
1f68d9c4 | 634 | out->dev = &pdev->dev; |
81b87f51 | 635 | out->id = OMAP_DSS_OUTPUT_HDMI; |
1f68d9c4 | 636 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; |
7286a08f | 637 | out->name = "hdmi.0"; |
2eea5ae6 | 638 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
0b450c31 | 639 | out->ops.hdmi = &hdmi_ops; |
b7328e14 | 640 | out->owner = THIS_MODULE; |
81b87f51 | 641 | |
5d47dbc8 | 642 | omapdss_register_output(out); |
81b87f51 AT |
643 | } |
644 | ||
645 | static void __exit hdmi_uninit_output(struct platform_device *pdev) | |
646 | { | |
1f68d9c4 | 647 | struct omap_dss_device *out = &hdmi.output; |
81b87f51 | 648 | |
5d47dbc8 | 649 | omapdss_unregister_output(out); |
81b87f51 AT |
650 | } |
651 | ||
2f5dc676 TV |
652 | static int hdmi_probe_of(struct platform_device *pdev) |
653 | { | |
654 | struct device_node *node = pdev->dev.of_node; | |
655 | struct device_node *ep; | |
656 | int r; | |
657 | ||
658 | ep = omapdss_of_get_first_endpoint(node); | |
659 | if (!ep) | |
660 | return 0; | |
661 | ||
662 | r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); | |
663 | if (r) | |
664 | goto err; | |
665 | ||
666 | of_node_put(ep); | |
667 | return 0; | |
668 | ||
669 | err: | |
670 | of_node_put(ep); | |
671 | return r; | |
672 | } | |
673 | ||
c3198a5e | 674 | /* HDMI HW IP initialisation */ |
17ae4e8c | 675 | static int omapdss_hdmihw_probe(struct platform_device *pdev) |
c3198a5e | 676 | { |
38f3daf6 | 677 | int r; |
dcf5f729 | 678 | int irq; |
c3198a5e | 679 | |
c3198a5e M |
680 | hdmi.pdev = pdev; |
681 | ||
682 | mutex_init(&hdmi.lock); | |
683 | ||
2f5dc676 TV |
684 | if (pdev->dev.of_node) { |
685 | r = hdmi_probe_of(pdev); | |
686 | if (r) | |
687 | return r; | |
688 | } | |
689 | ||
275cfa1a | 690 | r = hdmi_wp_init(pdev, &hdmi.wp); |
f382d9eb AT |
691 | if (r) |
692 | return r; | |
c3198a5e | 693 | |
275cfa1a | 694 | r = hdmi_pll_init(pdev, &hdmi.pll); |
c1577c1e AT |
695 | if (r) |
696 | return r; | |
697 | ||
275cfa1a | 698 | r = hdmi_phy_init(pdev, &hdmi.phy); |
5cac5aee AT |
699 | if (r) |
700 | return r; | |
ddb1d5ca | 701 | |
275cfa1a | 702 | r = hdmi4_core_init(pdev, &hdmi.core); |
425f02fd AT |
703 | if (r) |
704 | return r; | |
705 | ||
4fbafaf3 TV |
706 | r = hdmi_get_clocks(pdev); |
707 | if (r) { | |
47e443bc | 708 | DSSERR("can't get clocks\n"); |
4fbafaf3 TV |
709 | return r; |
710 | } | |
711 | ||
dcf5f729 TV |
712 | irq = platform_get_irq(pdev, 0); |
713 | if (irq < 0) { | |
714 | DSSERR("platform_get_irq failed\n"); | |
715 | return -ENODEV; | |
716 | } | |
717 | ||
718 | r = devm_request_threaded_irq(&pdev->dev, irq, | |
719 | NULL, hdmi_irq_handler, | |
720 | IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); | |
721 | if (r) { | |
722 | DSSERR("HDMI IRQ request failed\n"); | |
723 | return r; | |
724 | } | |
725 | ||
4fbafaf3 TV |
726 | pm_runtime_enable(&pdev->dev); |
727 | ||
002d368d TV |
728 | hdmi_init_output(pdev); |
729 | ||
e40402cf TV |
730 | dss_debugfs_create_file("hdmi", hdmi_dump_regs); |
731 | ||
cca35017 TV |
732 | return 0; |
733 | } | |
734 | ||
6e7e8f06 | 735 | static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) |
c3198a5e | 736 | { |
81b87f51 AT |
737 | hdmi_uninit_output(pdev); |
738 | ||
4fbafaf3 TV |
739 | pm_runtime_disable(&pdev->dev); |
740 | ||
c3198a5e M |
741 | return 0; |
742 | } | |
743 | ||
4fbafaf3 TV |
744 | static int hdmi_runtime_suspend(struct device *dev) |
745 | { | |
f11766d1 | 746 | clk_disable_unprepare(hdmi.sys_clk); |
4fbafaf3 TV |
747 | |
748 | dispc_runtime_put(); | |
4fbafaf3 TV |
749 | |
750 | return 0; | |
751 | } | |
752 | ||
753 | static int hdmi_runtime_resume(struct device *dev) | |
754 | { | |
755 | int r; | |
756 | ||
4fbafaf3 TV |
757 | r = dispc_runtime_get(); |
758 | if (r < 0) | |
852f0838 | 759 | return r; |
4fbafaf3 | 760 | |
f11766d1 | 761 | clk_prepare_enable(hdmi.sys_clk); |
4fbafaf3 TV |
762 | |
763 | return 0; | |
4fbafaf3 TV |
764 | } |
765 | ||
766 | static const struct dev_pm_ops hdmi_pm_ops = { | |
767 | .runtime_suspend = hdmi_runtime_suspend, | |
768 | .runtime_resume = hdmi_runtime_resume, | |
769 | }; | |
770 | ||
0465616d TV |
771 | static const struct of_device_id hdmi_of_match[] = { |
772 | { .compatible = "ti,omap4-hdmi", }, | |
773 | {}, | |
774 | }; | |
775 | ||
c3198a5e | 776 | static struct platform_driver omapdss_hdmihw_driver = { |
17ae4e8c | 777 | .probe = omapdss_hdmihw_probe, |
6e7e8f06 | 778 | .remove = __exit_p(omapdss_hdmihw_remove), |
c3198a5e M |
779 | .driver = { |
780 | .name = "omapdss_hdmi", | |
781 | .owner = THIS_MODULE, | |
4fbafaf3 | 782 | .pm = &hdmi_pm_ops, |
0465616d | 783 | .of_match_table = hdmi_of_match, |
c3198a5e M |
784 | }, |
785 | }; | |
786 | ||
ef26958a | 787 | int __init hdmi4_init_platform_driver(void) |
c3198a5e | 788 | { |
17ae4e8c | 789 | return platform_driver_register(&omapdss_hdmihw_driver); |
c3198a5e M |
790 | } |
791 | ||
ef26958a | 792 | void __exit hdmi4_uninit_platform_driver(void) |
c3198a5e | 793 | { |
04c742c3 | 794 | platform_driver_unregister(&omapdss_hdmihw_driver); |
c3198a5e | 795 | } |