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 | ||
e25001d8 TV |
84 | static int hdmi_init_regulator(void) |
85 | { | |
86 | struct regulator *reg; | |
87 | ||
88 | if (hdmi.vdda_hdmi_dac_reg != NULL) | |
89 | return 0; | |
90 | ||
931d4bd6 | 91 | reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); |
e25001d8 TV |
92 | |
93 | if (IS_ERR(reg)) { | |
40359a9b | 94 | if (PTR_ERR(reg) != -EPROBE_DEFER) |
931d4bd6 | 95 | DSSERR("can't get VDDA regulator\n"); |
e25001d8 TV |
96 | return PTR_ERR(reg); |
97 | } | |
98 | ||
99 | hdmi.vdda_hdmi_dac_reg = reg; | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
bb426fc9 | 104 | static int hdmi_power_on_core(struct omap_dss_device *dssdev) |
c3198a5e | 105 | { |
46095b2d | 106 | int r; |
c3198a5e | 107 | |
17486943 TV |
108 | r = regulator_enable(hdmi.vdda_hdmi_dac_reg); |
109 | if (r) | |
164ebdd1 | 110 | return r; |
17486943 | 111 | |
4fbafaf3 TV |
112 | r = hdmi_runtime_get(); |
113 | if (r) | |
cca35017 | 114 | goto err_runtime_get; |
c3198a5e | 115 | |
bb426fc9 TV |
116 | /* Make selection of HDMI in DSS */ |
117 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | |
118 | ||
0b450c31 TV |
119 | hdmi.core_enabled = true; |
120 | ||
bb426fc9 TV |
121 | return 0; |
122 | ||
123 | err_runtime_get: | |
124 | regulator_disable(hdmi.vdda_hdmi_dac_reg); | |
164ebdd1 | 125 | |
bb426fc9 TV |
126 | return r; |
127 | } | |
128 | ||
129 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) | |
130 | { | |
0b450c31 TV |
131 | hdmi.core_enabled = false; |
132 | ||
bb426fc9 TV |
133 | hdmi_runtime_put(); |
134 | regulator_disable(hdmi.vdda_hdmi_dac_reg); | |
bb426fc9 TV |
135 | } |
136 | ||
137 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |
138 | { | |
139 | int r; | |
140 | struct omap_video_timings *p; | |
7ae9a71e | 141 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
bb426fc9 TV |
142 | unsigned long phy; |
143 | ||
144 | r = hdmi_power_on_core(dssdev); | |
145 | if (r) | |
146 | return r; | |
147 | ||
275cfa1a | 148 | p = &hdmi.cfg.timings; |
c3198a5e | 149 | |
7849398f | 150 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); |
c3198a5e | 151 | |
d8d78941 TV |
152 | /* the functions below use kHz pixel clock. TODO: change to Hz */ |
153 | phy = p->pixelclock / 1000; | |
c3198a5e | 154 | |
275cfa1a | 155 | hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); |
c3198a5e | 156 | |
95a8aeb6 | 157 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ |
275cfa1a | 158 | r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); |
c3198a5e M |
159 | if (r) { |
160 | DSSDBG("Failed to lock PLL\n"); | |
cca35017 | 161 | goto err_pll_enable; |
c3198a5e M |
162 | } |
163 | ||
275cfa1a | 164 | r = hdmi_phy_enable(&hdmi.phy, &hdmi.wp, &hdmi.cfg); |
c3198a5e M |
165 | if (r) { |
166 | DSSDBG("Failed to start PHY\n"); | |
d3b4aa51 | 167 | goto err_phy_enable; |
c3198a5e M |
168 | } |
169 | ||
275cfa1a | 170 | hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); |
c3198a5e | 171 | |
c3198a5e M |
172 | /* bypass TV gamma table */ |
173 | dispc_enable_gamma_table(0); | |
174 | ||
175 | /* tv size */ | |
cea87b92 | 176 | dss_mgr_set_timings(mgr, p); |
c3198a5e | 177 | |
275cfa1a | 178 | r = hdmi_wp_video_start(&hdmi.wp); |
c0456be3 RN |
179 | if (r) |
180 | goto err_vid_enable; | |
c3198a5e | 181 | |
cea87b92 | 182 | r = dss_mgr_enable(mgr); |
33ca237f TV |
183 | if (r) |
184 | goto err_mgr_enable; | |
3870c909 | 185 | |
c3198a5e | 186 | return 0; |
33ca237f TV |
187 | |
188 | err_mgr_enable: | |
275cfa1a | 189 | hdmi_wp_video_stop(&hdmi.wp); |
c0456be3 | 190 | err_vid_enable: |
275cfa1a | 191 | hdmi_phy_disable(&hdmi.phy, &hdmi.wp); |
d3b4aa51 | 192 | err_phy_enable: |
275cfa1a | 193 | hdmi_pll_disable(&hdmi.pll, &hdmi.wp); |
cca35017 | 194 | err_pll_enable: |
bb426fc9 | 195 | hdmi_power_off_core(dssdev); |
c3198a5e M |
196 | return -EIO; |
197 | } | |
198 | ||
bb426fc9 | 199 | static void hdmi_power_off_full(struct omap_dss_device *dssdev) |
c3198a5e | 200 | { |
7ae9a71e | 201 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
cea87b92 AT |
202 | |
203 | dss_mgr_disable(mgr); | |
c3198a5e | 204 | |
275cfa1a AT |
205 | hdmi_wp_video_stop(&hdmi.wp); |
206 | hdmi_phy_disable(&hdmi.phy, &hdmi.wp); | |
207 | hdmi_pll_disable(&hdmi.pll, &hdmi.wp); | |
17486943 | 208 | |
bb426fc9 | 209 | hdmi_power_off_core(dssdev); |
c3198a5e M |
210 | } |
211 | ||
164ebdd1 | 212 | static int hdmi_display_check_timing(struct omap_dss_device *dssdev, |
c3198a5e M |
213 | struct omap_video_timings *timings) |
214 | { | |
1e676248 | 215 | struct omap_dss_device *out = &hdmi.output; |
c3198a5e | 216 | |
1e676248 | 217 | if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) |
c3198a5e | 218 | return -EINVAL; |
c3198a5e M |
219 | |
220 | return 0; | |
c3198a5e M |
221 | } |
222 | ||
164ebdd1 | 223 | static void hdmi_display_set_timing(struct omap_dss_device *dssdev, |
7849398f | 224 | struct omap_video_timings *timings) |
c3198a5e M |
225 | { |
226 | struct hdmi_cm cm; | |
7849398f | 227 | const struct hdmi_config *t; |
c3198a5e | 228 | |
ed1aa900 AT |
229 | mutex_lock(&hdmi.lock); |
230 | ||
7849398f | 231 | cm = hdmi_get_code(timings); |
275cfa1a | 232 | hdmi.cfg.cm = cm; |
7849398f | 233 | |
08d83e4e | 234 | t = hdmi_get_timings(cm.mode, cm.code); |
db680c65 | 235 | if (t != NULL) { |
275cfa1a | 236 | hdmi.cfg = *t; |
fa70dc5f | 237 | |
d8d78941 | 238 | dispc_set_tv_pclk(t->timings.pixelclock); |
1e676248 AT |
239 | } else { |
240 | hdmi.cfg.timings = *timings; | |
241 | hdmi.cfg.cm.code = 0; | |
242 | hdmi.cfg.cm.mode = HDMI_DVI; | |
243 | ||
d8d78941 | 244 | dispc_set_tv_pclk(timings->pixelclock); |
db680c65 | 245 | } |
5391e87d | 246 | |
1e676248 AT |
247 | DSSDBG("using mode: %s, code %d\n", hdmi.cfg.cm.mode == HDMI_DVI ? |
248 | "DVI" : "HDMI", hdmi.cfg.cm.code); | |
249 | ||
ed1aa900 | 250 | mutex_unlock(&hdmi.lock); |
c3198a5e M |
251 | } |
252 | ||
164ebdd1 | 253 | static void hdmi_display_get_timings(struct omap_dss_device *dssdev, |
0b450c31 TV |
254 | struct omap_video_timings *timings) |
255 | { | |
256 | const struct hdmi_config *cfg; | |
08d83e4e | 257 | struct hdmi_cm cm = hdmi.cfg.cm; |
0b450c31 | 258 | |
08d83e4e | 259 | cfg = hdmi_get_timings(cm.mode, cm.code); |
0b450c31 | 260 | if (cfg == NULL) |
08d83e4e | 261 | cfg = hdmi_default_timing(); |
0b450c31 TV |
262 | |
263 | memcpy(timings, &cfg->timings, sizeof(cfg->timings)); | |
264 | } | |
265 | ||
e40402cf | 266 | static void hdmi_dump_regs(struct seq_file *s) |
162874d5 M |
267 | { |
268 | mutex_lock(&hdmi.lock); | |
269 | ||
f8fb7d7b WY |
270 | if (hdmi_runtime_get()) { |
271 | mutex_unlock(&hdmi.lock); | |
162874d5 | 272 | return; |
f8fb7d7b | 273 | } |
162874d5 | 274 | |
275cfa1a AT |
275 | hdmi_wp_dump(&hdmi.wp, s); |
276 | hdmi_pll_dump(&hdmi.pll, s); | |
277 | hdmi_phy_dump(&hdmi.phy, s); | |
278 | hdmi4_core_dump(&hdmi.core, s); | |
162874d5 M |
279 | |
280 | hdmi_runtime_put(); | |
281 | mutex_unlock(&hdmi.lock); | |
282 | } | |
283 | ||
164ebdd1 | 284 | static int read_edid(u8 *buf, int len) |
47024565 TV |
285 | { |
286 | int r; | |
287 | ||
288 | mutex_lock(&hdmi.lock); | |
289 | ||
290 | r = hdmi_runtime_get(); | |
291 | BUG_ON(r); | |
292 | ||
275cfa1a | 293 | r = hdmi4_read_edid(&hdmi.core, buf, len); |
47024565 TV |
294 | |
295 | hdmi_runtime_put(); | |
296 | mutex_unlock(&hdmi.lock); | |
297 | ||
298 | return r; | |
299 | } | |
300 | ||
164ebdd1 | 301 | static int hdmi_display_enable(struct omap_dss_device *dssdev) |
c3198a5e | 302 | { |
1f68d9c4 | 303 | struct omap_dss_device *out = &hdmi.output; |
c3198a5e M |
304 | int r = 0; |
305 | ||
306 | DSSDBG("ENTER hdmi_display_enable\n"); | |
307 | ||
308 | mutex_lock(&hdmi.lock); | |
309 | ||
cea87b92 AT |
310 | if (out == NULL || out->manager == NULL) { |
311 | DSSERR("failed to enable display: no output/manager\n"); | |
05e1d606 TV |
312 | r = -ENODEV; |
313 | goto err0; | |
314 | } | |
315 | ||
bb426fc9 | 316 | r = hdmi_power_on_full(dssdev); |
c3198a5e M |
317 | if (r) { |
318 | DSSERR("failed to power on device\n"); | |
d3923933 | 319 | goto err0; |
c3198a5e M |
320 | } |
321 | ||
322 | mutex_unlock(&hdmi.lock); | |
323 | return 0; | |
324 | ||
c3198a5e M |
325 | err0: |
326 | mutex_unlock(&hdmi.lock); | |
327 | return r; | |
328 | } | |
329 | ||
164ebdd1 | 330 | static void hdmi_display_disable(struct omap_dss_device *dssdev) |
c3198a5e M |
331 | { |
332 | DSSDBG("Enter hdmi_display_disable\n"); | |
333 | ||
334 | mutex_lock(&hdmi.lock); | |
335 | ||
bb426fc9 | 336 | hdmi_power_off_full(dssdev); |
c3198a5e | 337 | |
c3198a5e M |
338 | mutex_unlock(&hdmi.lock); |
339 | } | |
340 | ||
164ebdd1 | 341 | static int hdmi_core_enable(struct omap_dss_device *dssdev) |
4489823c TV |
342 | { |
343 | int r = 0; | |
344 | ||
345 | DSSDBG("ENTER omapdss_hdmi_core_enable\n"); | |
346 | ||
347 | mutex_lock(&hdmi.lock); | |
348 | ||
4489823c TV |
349 | r = hdmi_power_on_core(dssdev); |
350 | if (r) { | |
351 | DSSERR("failed to power on device\n"); | |
352 | goto err0; | |
353 | } | |
354 | ||
355 | mutex_unlock(&hdmi.lock); | |
356 | return 0; | |
357 | ||
358 | err0: | |
359 | mutex_unlock(&hdmi.lock); | |
360 | return r; | |
361 | } | |
362 | ||
164ebdd1 | 363 | static void hdmi_core_disable(struct omap_dss_device *dssdev) |
4489823c TV |
364 | { |
365 | DSSDBG("Enter omapdss_hdmi_core_disable\n"); | |
366 | ||
367 | mutex_lock(&hdmi.lock); | |
368 | ||
369 | hdmi_power_off_core(dssdev); | |
370 | ||
371 | mutex_unlock(&hdmi.lock); | |
372 | } | |
373 | ||
4fbafaf3 TV |
374 | static int hdmi_get_clocks(struct platform_device *pdev) |
375 | { | |
376 | struct clk *clk; | |
377 | ||
b2c9c8ee | 378 | clk = devm_clk_get(&pdev->dev, "sys_clk"); |
4fbafaf3 TV |
379 | if (IS_ERR(clk)) { |
380 | DSSERR("can't get sys_clk\n"); | |
381 | return PTR_ERR(clk); | |
382 | } | |
383 | ||
384 | hdmi.sys_clk = clk; | |
385 | ||
4fbafaf3 TV |
386 | return 0; |
387 | } | |
388 | ||
0b450c31 TV |
389 | static int hdmi_connect(struct omap_dss_device *dssdev, |
390 | struct omap_dss_device *dst) | |
391 | { | |
392 | struct omap_overlay_manager *mgr; | |
393 | int r; | |
394 | ||
0b450c31 TV |
395 | r = hdmi_init_regulator(); |
396 | if (r) | |
397 | return r; | |
398 | ||
399 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | |
400 | if (!mgr) | |
401 | return -ENODEV; | |
402 | ||
403 | r = dss_mgr_connect(mgr, dssdev); | |
404 | if (r) | |
405 | return r; | |
406 | ||
407 | r = omapdss_output_set_device(dssdev, dst); | |
408 | if (r) { | |
409 | DSSERR("failed to connect output to new device: %s\n", | |
410 | dst->name); | |
411 | dss_mgr_disconnect(mgr, dssdev); | |
412 | return r; | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
418 | static void hdmi_disconnect(struct omap_dss_device *dssdev, | |
419 | struct omap_dss_device *dst) | |
420 | { | |
9560dc10 | 421 | WARN_ON(dst != dssdev->dst); |
0b450c31 | 422 | |
9560dc10 | 423 | if (dst != dssdev->dst) |
0b450c31 TV |
424 | return; |
425 | ||
426 | omapdss_output_unset_device(dssdev); | |
427 | ||
428 | if (dssdev->manager) | |
429 | dss_mgr_disconnect(dssdev->manager, dssdev); | |
430 | } | |
431 | ||
432 | static int hdmi_read_edid(struct omap_dss_device *dssdev, | |
433 | u8 *edid, int len) | |
434 | { | |
435 | bool need_enable; | |
436 | int r; | |
437 | ||
438 | need_enable = hdmi.core_enabled == false; | |
439 | ||
440 | if (need_enable) { | |
164ebdd1 | 441 | r = hdmi_core_enable(dssdev); |
0b450c31 TV |
442 | if (r) |
443 | return r; | |
444 | } | |
445 | ||
164ebdd1 | 446 | r = read_edid(edid, len); |
0b450c31 TV |
447 | |
448 | if (need_enable) | |
164ebdd1 | 449 | hdmi_core_disable(dssdev); |
0b450c31 TV |
450 | |
451 | return r; | |
452 | } | |
453 | ||
454 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | |
164ebdd1 | 455 | static int hdmi_audio_enable(struct omap_dss_device *dssdev) |
0b450c31 TV |
456 | { |
457 | int r; | |
458 | ||
459 | mutex_lock(&hdmi.lock); | |
460 | ||
08d83e4e | 461 | if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) { |
0b450c31 TV |
462 | r = -EPERM; |
463 | goto err; | |
464 | } | |
465 | ||
275cfa1a | 466 | r = hdmi_wp_audio_enable(&hdmi.wp, true); |
0b450c31 TV |
467 | if (r) |
468 | goto err; | |
469 | ||
470 | mutex_unlock(&hdmi.lock); | |
471 | return 0; | |
472 | ||
473 | err: | |
474 | mutex_unlock(&hdmi.lock); | |
475 | return r; | |
476 | } | |
477 | ||
164ebdd1 | 478 | static void hdmi_audio_disable(struct omap_dss_device *dssdev) |
0b450c31 | 479 | { |
275cfa1a | 480 | hdmi_wp_audio_enable(&hdmi.wp, false); |
0b450c31 TV |
481 | } |
482 | ||
164ebdd1 | 483 | static int hdmi_audio_start(struct omap_dss_device *dssdev) |
0b450c31 | 484 | { |
275cfa1a | 485 | return hdmi4_audio_start(&hdmi.core, &hdmi.wp); |
0b450c31 TV |
486 | } |
487 | ||
164ebdd1 | 488 | static void hdmi_audio_stop(struct omap_dss_device *dssdev) |
0b450c31 | 489 | { |
275cfa1a | 490 | hdmi4_audio_stop(&hdmi.core, &hdmi.wp); |
0b450c31 TV |
491 | } |
492 | ||
164ebdd1 | 493 | static bool hdmi_audio_supported(struct omap_dss_device *dssdev) |
0b450c31 TV |
494 | { |
495 | bool r; | |
496 | ||
497 | mutex_lock(&hdmi.lock); | |
498 | ||
08d83e4e | 499 | r = hdmi_mode_has_audio(hdmi.cfg.cm.mode); |
0b450c31 TV |
500 | |
501 | mutex_unlock(&hdmi.lock); | |
502 | return r; | |
503 | } | |
504 | ||
164ebdd1 | 505 | static int hdmi_audio_config(struct omap_dss_device *dssdev, |
0b450c31 TV |
506 | struct omap_dss_audio *audio) |
507 | { | |
508 | int r; | |
d8d78941 | 509 | u32 pclk = hdmi.cfg.timings.pixelclock; |
0b450c31 TV |
510 | |
511 | mutex_lock(&hdmi.lock); | |
512 | ||
08d83e4e | 513 | if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) { |
0b450c31 TV |
514 | r = -EPERM; |
515 | goto err; | |
516 | } | |
517 | ||
08d83e4e | 518 | r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); |
0b450c31 TV |
519 | if (r) |
520 | goto err; | |
521 | ||
522 | mutex_unlock(&hdmi.lock); | |
523 | return 0; | |
524 | ||
525 | err: | |
526 | mutex_unlock(&hdmi.lock); | |
527 | return r; | |
528 | } | |
529 | #else | |
164ebdd1 | 530 | static int hdmi_audio_enable(struct omap_dss_device *dssdev) |
0b450c31 TV |
531 | { |
532 | return -EPERM; | |
533 | } | |
534 | ||
164ebdd1 | 535 | static void hdmi_audio_disable(struct omap_dss_device *dssdev) |
0b450c31 TV |
536 | { |
537 | } | |
538 | ||
164ebdd1 | 539 | static int hdmi_audio_start(struct omap_dss_device *dssdev) |
0b450c31 TV |
540 | { |
541 | return -EPERM; | |
542 | } | |
543 | ||
164ebdd1 | 544 | static void hdmi_audio_stop(struct omap_dss_device *dssdev) |
0b450c31 TV |
545 | { |
546 | } | |
547 | ||
164ebdd1 | 548 | static bool hdmi_audio_supported(struct omap_dss_device *dssdev) |
0b450c31 TV |
549 | { |
550 | return false; | |
551 | } | |
552 | ||
164ebdd1 | 553 | static int hdmi_audio_config(struct omap_dss_device *dssdev, |
0b450c31 TV |
554 | struct omap_dss_audio *audio) |
555 | { | |
556 | return -EPERM; | |
557 | } | |
558 | #endif | |
559 | ||
560 | static const struct omapdss_hdmi_ops hdmi_ops = { | |
561 | .connect = hdmi_connect, | |
562 | .disconnect = hdmi_disconnect, | |
563 | ||
164ebdd1 TV |
564 | .enable = hdmi_display_enable, |
565 | .disable = hdmi_display_disable, | |
0b450c31 | 566 | |
164ebdd1 TV |
567 | .check_timings = hdmi_display_check_timing, |
568 | .set_timings = hdmi_display_set_timing, | |
569 | .get_timings = hdmi_display_get_timings, | |
0b450c31 TV |
570 | |
571 | .read_edid = hdmi_read_edid, | |
572 | ||
164ebdd1 TV |
573 | .audio_enable = hdmi_audio_enable, |
574 | .audio_disable = hdmi_audio_disable, | |
575 | .audio_start = hdmi_audio_start, | |
576 | .audio_stop = hdmi_audio_stop, | |
577 | .audio_supported = hdmi_audio_supported, | |
578 | .audio_config = hdmi_audio_config, | |
0b450c31 TV |
579 | }; |
580 | ||
17ae4e8c | 581 | static void hdmi_init_output(struct platform_device *pdev) |
81b87f51 | 582 | { |
1f68d9c4 | 583 | struct omap_dss_device *out = &hdmi.output; |
81b87f51 | 584 | |
1f68d9c4 | 585 | out->dev = &pdev->dev; |
81b87f51 | 586 | out->id = OMAP_DSS_OUTPUT_HDMI; |
1f68d9c4 | 587 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; |
7286a08f | 588 | out->name = "hdmi.0"; |
2eea5ae6 | 589 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
0b450c31 | 590 | out->ops.hdmi = &hdmi_ops; |
b7328e14 | 591 | out->owner = THIS_MODULE; |
81b87f51 | 592 | |
5d47dbc8 | 593 | omapdss_register_output(out); |
81b87f51 AT |
594 | } |
595 | ||
596 | static void __exit hdmi_uninit_output(struct platform_device *pdev) | |
597 | { | |
1f68d9c4 | 598 | struct omap_dss_device *out = &hdmi.output; |
81b87f51 | 599 | |
5d47dbc8 | 600 | omapdss_unregister_output(out); |
81b87f51 AT |
601 | } |
602 | ||
2f5dc676 TV |
603 | static int hdmi_probe_of(struct platform_device *pdev) |
604 | { | |
605 | struct device_node *node = pdev->dev.of_node; | |
606 | struct device_node *ep; | |
607 | int r; | |
608 | ||
609 | ep = omapdss_of_get_first_endpoint(node); | |
610 | if (!ep) | |
611 | return 0; | |
612 | ||
613 | r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); | |
614 | if (r) | |
615 | goto err; | |
616 | ||
617 | of_node_put(ep); | |
618 | return 0; | |
619 | ||
620 | err: | |
621 | of_node_put(ep); | |
622 | return r; | |
623 | } | |
624 | ||
c3198a5e | 625 | /* HDMI HW IP initialisation */ |
17ae4e8c | 626 | static int omapdss_hdmihw_probe(struct platform_device *pdev) |
c3198a5e | 627 | { |
38f3daf6 | 628 | int r; |
c3198a5e | 629 | |
c3198a5e M |
630 | hdmi.pdev = pdev; |
631 | ||
632 | mutex_init(&hdmi.lock); | |
633 | ||
2f5dc676 TV |
634 | if (pdev->dev.of_node) { |
635 | r = hdmi_probe_of(pdev); | |
636 | if (r) | |
637 | return r; | |
638 | } | |
639 | ||
275cfa1a | 640 | r = hdmi_wp_init(pdev, &hdmi.wp); |
f382d9eb AT |
641 | if (r) |
642 | return r; | |
c3198a5e | 643 | |
275cfa1a | 644 | r = hdmi_pll_init(pdev, &hdmi.pll); |
c1577c1e AT |
645 | if (r) |
646 | return r; | |
647 | ||
275cfa1a | 648 | r = hdmi_phy_init(pdev, &hdmi.phy); |
5cac5aee AT |
649 | if (r) |
650 | return r; | |
ddb1d5ca | 651 | |
275cfa1a | 652 | r = hdmi4_core_init(pdev, &hdmi.core); |
425f02fd AT |
653 | if (r) |
654 | return r; | |
655 | ||
4fbafaf3 TV |
656 | r = hdmi_get_clocks(pdev); |
657 | if (r) { | |
47e443bc | 658 | DSSERR("can't get clocks\n"); |
4fbafaf3 TV |
659 | return r; |
660 | } | |
661 | ||
662 | pm_runtime_enable(&pdev->dev); | |
663 | ||
002d368d TV |
664 | hdmi_init_output(pdev); |
665 | ||
e40402cf TV |
666 | dss_debugfs_create_file("hdmi", hdmi_dump_regs); |
667 | ||
cca35017 TV |
668 | return 0; |
669 | } | |
670 | ||
6e7e8f06 | 671 | static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) |
c3198a5e | 672 | { |
81b87f51 AT |
673 | hdmi_uninit_output(pdev); |
674 | ||
4fbafaf3 TV |
675 | pm_runtime_disable(&pdev->dev); |
676 | ||
c3198a5e M |
677 | return 0; |
678 | } | |
679 | ||
4fbafaf3 TV |
680 | static int hdmi_runtime_suspend(struct device *dev) |
681 | { | |
f11766d1 | 682 | clk_disable_unprepare(hdmi.sys_clk); |
4fbafaf3 TV |
683 | |
684 | dispc_runtime_put(); | |
4fbafaf3 TV |
685 | |
686 | return 0; | |
687 | } | |
688 | ||
689 | static int hdmi_runtime_resume(struct device *dev) | |
690 | { | |
691 | int r; | |
692 | ||
4fbafaf3 TV |
693 | r = dispc_runtime_get(); |
694 | if (r < 0) | |
852f0838 | 695 | return r; |
4fbafaf3 | 696 | |
f11766d1 | 697 | clk_prepare_enable(hdmi.sys_clk); |
4fbafaf3 TV |
698 | |
699 | return 0; | |
4fbafaf3 TV |
700 | } |
701 | ||
702 | static const struct dev_pm_ops hdmi_pm_ops = { | |
703 | .runtime_suspend = hdmi_runtime_suspend, | |
704 | .runtime_resume = hdmi_runtime_resume, | |
705 | }; | |
706 | ||
0465616d TV |
707 | static const struct of_device_id hdmi_of_match[] = { |
708 | { .compatible = "ti,omap4-hdmi", }, | |
709 | {}, | |
710 | }; | |
711 | ||
c3198a5e | 712 | static struct platform_driver omapdss_hdmihw_driver = { |
17ae4e8c | 713 | .probe = omapdss_hdmihw_probe, |
6e7e8f06 | 714 | .remove = __exit_p(omapdss_hdmihw_remove), |
c3198a5e M |
715 | .driver = { |
716 | .name = "omapdss_hdmi", | |
717 | .owner = THIS_MODULE, | |
4fbafaf3 | 718 | .pm = &hdmi_pm_ops, |
0465616d | 719 | .of_match_table = hdmi_of_match, |
c3198a5e M |
720 | }, |
721 | }; | |
722 | ||
ef26958a | 723 | int __init hdmi4_init_platform_driver(void) |
c3198a5e | 724 | { |
17ae4e8c | 725 | return platform_driver_register(&omapdss_hdmihw_driver); |
c3198a5e M |
726 | } |
727 | ||
ef26958a | 728 | void __exit hdmi4_uninit_platform_driver(void) |
c3198a5e | 729 | { |
04c742c3 | 730 | platform_driver_unregister(&omapdss_hdmihw_driver); |
c3198a5e | 731 | } |