blob: 6e5894fb96cedc86825f24263f1dfdf340f0b1d9 [file] [log] [blame]
Tomi Valkeinenf5bab222014-03-13 12:44:14 +02001/*
2 * HDMI driver for OMAP5
3 *
4 * Copyright (C) 2014 Texas Instruments Incorporated
5 *
6 * Authors:
7 * Yong Zhi
8 * Mythri pk
9 * Archit Taneja <archit@ti.com>
10 * Tomi Valkeinen <tomi.valkeinen@ti.com>
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License version 2 as published by
14 * the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25#define DSS_SUBSYS_NAME "HDMI"
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/err.h>
30#include <linux/io.h>
31#include <linux/interrupt.h>
32#include <linux/mutex.h>
33#include <linux/delay.h>
34#include <linux/string.h>
35#include <linux/platform_device.h>
36#include <linux/pm_runtime.h>
37#include <linux/clk.h>
38#include <linux/gpio.h>
39#include <linux/regulator/consumer.h>
40#include <video/omapdss.h>
41
42#include "hdmi5_core.h"
43#include "dss.h"
44#include "dss_features.h"
45
Jyri Sarha945514b2014-06-27 16:47:00 +030046static struct omap_hdmi hdmi;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +020047
48static int hdmi_runtime_get(void)
49{
50 int r;
51
52 DSSDBG("hdmi_runtime_get\n");
53
54 r = pm_runtime_get_sync(&hdmi.pdev->dev);
55 WARN_ON(r < 0);
56 if (r < 0)
57 return r;
58
59 return 0;
60}
61
62static void hdmi_runtime_put(void)
63{
64 int r;
65
66 DSSDBG("hdmi_runtime_put\n");
67
68 r = pm_runtime_put_sync(&hdmi.pdev->dev);
69 WARN_ON(r < 0 && r != -ENOSYS);
70}
71
72static irqreturn_t hdmi_irq_handler(int irq, void *data)
73{
74 struct hdmi_wp_data *wp = data;
75 u32 irqstatus;
76
77 irqstatus = hdmi_wp_get_irqstatus(wp);
78 hdmi_wp_set_irqstatus(wp, irqstatus);
79
80 if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
81 irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
82 u32 v;
83 /*
84 * If we get both connect and disconnect interrupts at the same
85 * time, turn off the PHY, clear interrupts, and restart, which
86 * raises connect interrupt if a cable is connected, or nothing
87 * if cable is not connected.
88 */
89
90 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
91
92 /*
93 * We always get bogus CONNECT & DISCONNECT interrupts when
94 * setting the PHY to LDOON. To ignore those, we force the RXDET
95 * line to 0 until the PHY power state has been changed.
96 */
97 v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL);
98 v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */
99 v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */
100 hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v);
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
107 REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15);
108
109 } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
110 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
111 } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
112 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
113 }
114
115 return IRQ_HANDLED;
116}
117
118static int hdmi_init_regulator(void)
119{
120 int r;
121 struct regulator *reg;
122
123 if (hdmi.vdda_reg != NULL)
124 return 0;
125
126 reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
127 if (IS_ERR(reg)) {
128 DSSERR("can't get VDDA regulator\n");
129 return PTR_ERR(reg);
130 }
131
132 if (regulator_can_change_voltage(reg)) {
133 r = regulator_set_voltage(reg, 1800000, 1800000);
134 if (r) {
135 devm_regulator_put(reg);
136 DSSWARN("can't set the regulator voltage\n");
137 return r;
138 }
139 }
140
141 hdmi.vdda_reg = reg;
142
143 return 0;
144}
145
146static int hdmi_power_on_core(struct omap_dss_device *dssdev)
147{
148 int r;
149
150 r = regulator_enable(hdmi.vdda_reg);
151 if (r)
152 return r;
153
154 r = hdmi_runtime_get();
155 if (r)
156 goto err_runtime_get;
157
158 /* Make selection of HDMI in DSS */
159 dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
160
161 hdmi.core_enabled = true;
162
163 return 0;
164
165err_runtime_get:
166 regulator_disable(hdmi.vdda_reg);
167
168 return r;
169}
170
171static void hdmi_power_off_core(struct omap_dss_device *dssdev)
172{
173 hdmi.core_enabled = false;
174
175 hdmi_runtime_put();
176 regulator_disable(hdmi.vdda_reg);
177}
178
179static int hdmi_power_on_full(struct omap_dss_device *dssdev)
180{
181 int r;
182 struct omap_video_timings *p;
183 struct omap_overlay_manager *mgr = hdmi.output.manager;
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300184 struct dss_pll_clock_info hdmi_cinfo = { 0 };
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200185
186 r = hdmi_power_on_core(dssdev);
187 if (r)
188 return r;
189
190 p = &hdmi.cfg.timings;
191
192 DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
193
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300194 hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200195
196 /* disable and clear irqs */
197 hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
198 hdmi_wp_set_irqstatus(&hdmi.wp,
199 hdmi_wp_get_irqstatus(&hdmi.wp));
200
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300201 r = dss_pll_enable(&hdmi.pll.pll);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200202 if (r) {
Tomi Valkeinenc2fbd062014-10-16 16:01:51 +0300203 DSSERR("Failed to enable PLL\n");
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200204 goto err_pll_enable;
205 }
206
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300207 r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
Tomi Valkeinenc2fbd062014-10-16 16:01:51 +0300208 if (r) {
209 DSSERR("Failed to configure PLL\n");
210 goto err_pll_cfg;
211 }
212
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300213 r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
214 hdmi_cinfo.clkout[0]);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200215 if (r) {
216 DSSDBG("Failed to start PHY\n");
217 goto err_phy_cfg;
218 }
219
220 r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON);
221 if (r)
222 goto err_phy_pwr;
223
224 hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
225
226 /* bypass TV gamma table */
227 dispc_enable_gamma_table(0);
228
229 /* tv size */
230 dss_mgr_set_timings(mgr, p);
231
232 r = hdmi_wp_video_start(&hdmi.wp);
233 if (r)
234 goto err_vid_enable;
235
236 r = dss_mgr_enable(mgr);
237 if (r)
238 goto err_mgr_enable;
239
240 hdmi_wp_set_irqenable(&hdmi.wp,
241 HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
242
243 return 0;
244
245err_mgr_enable:
246 hdmi_wp_video_stop(&hdmi.wp);
247err_vid_enable:
248 hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
249err_phy_pwr:
250err_phy_cfg:
Tomi Valkeinenc2fbd062014-10-16 16:01:51 +0300251err_pll_cfg:
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300252 dss_pll_disable(&hdmi.pll.pll);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200253err_pll_enable:
254 hdmi_power_off_core(dssdev);
255 return -EIO;
256}
257
258static void hdmi_power_off_full(struct omap_dss_device *dssdev)
259{
260 struct omap_overlay_manager *mgr = hdmi.output.manager;
261
262 hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
263
264 dss_mgr_disable(mgr);
265
266 hdmi_wp_video_stop(&hdmi.wp);
267
268 hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
269
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300270 dss_pll_disable(&hdmi.pll.pll);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200271
272 hdmi_power_off_core(dssdev);
273}
274
275static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
276 struct omap_video_timings *timings)
277{
278 struct omap_dss_device *out = &hdmi.output;
279
Tomi Valkeinen31dd0f42014-09-16 12:46:33 +0300280 /* TODO: proper interlace support */
281 if (timings->interlace)
282 return -EINVAL;
283
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200284 if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
285 return -EINVAL;
286
287 return 0;
288}
289
290static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
291 struct omap_video_timings *timings)
292{
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200293 mutex_lock(&hdmi.lock);
294
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300295 hdmi.cfg.timings = *timings;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200296
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300297 dispc_set_tv_pclk(timings->pixelclock);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200298
299 mutex_unlock(&hdmi.lock);
300}
301
302static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
303 struct omap_video_timings *timings)
304{
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300305 *timings = hdmi.cfg.timings;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200306}
307
308static void hdmi_dump_regs(struct seq_file *s)
309{
310 mutex_lock(&hdmi.lock);
311
312 if (hdmi_runtime_get()) {
313 mutex_unlock(&hdmi.lock);
314 return;
315 }
316
317 hdmi_wp_dump(&hdmi.wp, s);
318 hdmi_pll_dump(&hdmi.pll, s);
319 hdmi_phy_dump(&hdmi.phy, s);
320 hdmi5_core_dump(&hdmi.core, s);
321
322 hdmi_runtime_put();
323 mutex_unlock(&hdmi.lock);
324}
325
326static int read_edid(u8 *buf, int len)
327{
328 int r;
329 int idlemode;
330
331 mutex_lock(&hdmi.lock);
332
333 r = hdmi_runtime_get();
334 BUG_ON(r);
335
336 idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
337 /* No-idle mode */
338 REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
339
340 r = hdmi5_read_edid(&hdmi.core, buf, len);
341
342 REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
343
344 hdmi_runtime_put();
345 mutex_unlock(&hdmi.lock);
346
347 return r;
348}
349
350static int hdmi_display_enable(struct omap_dss_device *dssdev)
351{
352 struct omap_dss_device *out = &hdmi.output;
353 int r = 0;
354
355 DSSDBG("ENTER hdmi_display_enable\n");
356
357 mutex_lock(&hdmi.lock);
358
359 if (out == NULL || out->manager == NULL) {
360 DSSERR("failed to enable display: no output/manager\n");
361 r = -ENODEV;
362 goto err0;
363 }
364
365 r = hdmi_power_on_full(dssdev);
366 if (r) {
367 DSSERR("failed to power on device\n");
368 goto err0;
369 }
370
371 mutex_unlock(&hdmi.lock);
372 return 0;
373
374err0:
375 mutex_unlock(&hdmi.lock);
376 return r;
377}
378
379static void hdmi_display_disable(struct omap_dss_device *dssdev)
380{
381 DSSDBG("Enter hdmi_display_disable\n");
382
383 mutex_lock(&hdmi.lock);
384
385 hdmi_power_off_full(dssdev);
386
387 mutex_unlock(&hdmi.lock);
388}
389
390static int hdmi_core_enable(struct omap_dss_device *dssdev)
391{
392 int r = 0;
393
394 DSSDBG("ENTER omapdss_hdmi_core_enable\n");
395
396 mutex_lock(&hdmi.lock);
397
398 r = hdmi_power_on_core(dssdev);
399 if (r) {
400 DSSERR("failed to power on device\n");
401 goto err0;
402 }
403
404 mutex_unlock(&hdmi.lock);
405 return 0;
406
407err0:
408 mutex_unlock(&hdmi.lock);
409 return r;
410}
411
412static void hdmi_core_disable(struct omap_dss_device *dssdev)
413{
414 DSSDBG("Enter omapdss_hdmi_core_disable\n");
415
416 mutex_lock(&hdmi.lock);
417
418 hdmi_power_off_core(dssdev);
419
420 mutex_unlock(&hdmi.lock);
421}
422
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200423static int hdmi_connect(struct omap_dss_device *dssdev,
424 struct omap_dss_device *dst)
425{
426 struct omap_overlay_manager *mgr;
427 int r;
428
429 r = hdmi_init_regulator();
430 if (r)
431 return r;
432
433 mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
434 if (!mgr)
435 return -ENODEV;
436
437 r = dss_mgr_connect(mgr, dssdev);
438 if (r)
439 return r;
440
441 r = omapdss_output_set_device(dssdev, dst);
442 if (r) {
443 DSSERR("failed to connect output to new device: %s\n",
444 dst->name);
445 dss_mgr_disconnect(mgr, dssdev);
446 return r;
447 }
448
449 return 0;
450}
451
452static void hdmi_disconnect(struct omap_dss_device *dssdev,
453 struct omap_dss_device *dst)
454{
455 WARN_ON(dst != dssdev->dst);
456
457 if (dst != dssdev->dst)
458 return;
459
460 omapdss_output_unset_device(dssdev);
461
462 if (dssdev->manager)
463 dss_mgr_disconnect(dssdev->manager, dssdev);
464}
465
466static int hdmi_read_edid(struct omap_dss_device *dssdev,
467 u8 *edid, int len)
468{
469 bool need_enable;
470 int r;
471
472 need_enable = hdmi.core_enabled == false;
473
474 if (need_enable) {
475 r = hdmi_core_enable(dssdev);
476 if (r)
477 return r;
478 }
479
480 r = read_edid(edid, len);
481
482 if (need_enable)
483 hdmi_core_disable(dssdev);
484
485 return r;
486}
487
488#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
489static int hdmi_audio_enable(struct omap_dss_device *dssdev)
490{
491 int r;
492
493 mutex_lock(&hdmi.lock);
494
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300495 if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) {
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200496 r = -EPERM;
497 goto err;
498 }
499
500 r = hdmi_wp_audio_enable(&hdmi.wp, true);
501 if (r)
502 goto err;
503
504 mutex_unlock(&hdmi.lock);
505 return 0;
506
507err:
508 mutex_unlock(&hdmi.lock);
509 return r;
510}
511
512static void hdmi_audio_disable(struct omap_dss_device *dssdev)
513{
514 hdmi_wp_audio_enable(&hdmi.wp, false);
515}
516
517static int hdmi_audio_start(struct omap_dss_device *dssdev)
518{
519 return hdmi_wp_audio_core_req_enable(&hdmi.wp, true);
520}
521
522static void hdmi_audio_stop(struct omap_dss_device *dssdev)
523{
524 hdmi_wp_audio_core_req_enable(&hdmi.wp, false);
525}
526
527static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
528{
529 bool r;
530
531 mutex_lock(&hdmi.lock);
532
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300533 r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200534
535 mutex_unlock(&hdmi.lock);
536 return r;
537}
538
539static int hdmi_audio_config(struct omap_dss_device *dssdev,
540 struct omap_dss_audio *audio)
541{
542 int r;
543 u32 pclk = hdmi.cfg.timings.pixelclock;
544
545 mutex_lock(&hdmi.lock);
546
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300547 if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) {
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200548 r = -EPERM;
549 goto err;
550 }
551
552 r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, audio, pclk);
553 if (r)
554 goto err;
555
556 mutex_unlock(&hdmi.lock);
557 return 0;
558
559err:
560 mutex_unlock(&hdmi.lock);
561 return r;
562}
563#else
564static int hdmi_audio_enable(struct omap_dss_device *dssdev)
565{
566 return -EPERM;
567}
568
569static void hdmi_audio_disable(struct omap_dss_device *dssdev)
570{
571}
572
573static int hdmi_audio_start(struct omap_dss_device *dssdev)
574{
575 return -EPERM;
576}
577
578static void hdmi_audio_stop(struct omap_dss_device *dssdev)
579{
580}
581
582static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
583{
584 return false;
585}
586
587static int hdmi_audio_config(struct omap_dss_device *dssdev,
588 struct omap_dss_audio *audio)
589{
590 return -EPERM;
591}
592#endif
593
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300594static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
595 const struct hdmi_avi_infoframe *avi)
596{
597 hdmi.cfg.infoframe = *avi;
598 return 0;
599}
600
601static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
602 bool hdmi_mode)
603{
604 hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
605 return 0;
606}
607
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200608static const struct omapdss_hdmi_ops hdmi_ops = {
609 .connect = hdmi_connect,
610 .disconnect = hdmi_disconnect,
611
612 .enable = hdmi_display_enable,
613 .disable = hdmi_display_disable,
614
615 .check_timings = hdmi_display_check_timing,
616 .set_timings = hdmi_display_set_timing,
617 .get_timings = hdmi_display_get_timings,
618
619 .read_edid = hdmi_read_edid,
Tomi Valkeinen769dcb12014-06-18 14:21:55 +0300620 .set_infoframe = hdmi_set_infoframe,
621 .set_hdmi_mode = hdmi_set_hdmi_mode,
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200622
623 .audio_enable = hdmi_audio_enable,
624 .audio_disable = hdmi_audio_disable,
625 .audio_start = hdmi_audio_start,
626 .audio_stop = hdmi_audio_stop,
627 .audio_supported = hdmi_audio_supported,
628 .audio_config = hdmi_audio_config,
629};
630
631static void hdmi_init_output(struct platform_device *pdev)
632{
633 struct omap_dss_device *out = &hdmi.output;
634
635 out->dev = &pdev->dev;
636 out->id = OMAP_DSS_OUTPUT_HDMI;
637 out->output_type = OMAP_DISPLAY_TYPE_HDMI;
638 out->name = "hdmi.0";
639 out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
640 out->ops.hdmi = &hdmi_ops;
641 out->owner = THIS_MODULE;
642
643 omapdss_register_output(out);
644}
645
646static void __exit hdmi_uninit_output(struct platform_device *pdev)
647{
648 struct omap_dss_device *out = &hdmi.output;
649
650 omapdss_unregister_output(out);
651}
652
653static int hdmi_probe_of(struct platform_device *pdev)
654{
655 struct device_node *node = pdev->dev.of_node;
656 struct device_node *ep;
657 int r;
658
659 ep = omapdss_of_get_first_endpoint(node);
660 if (!ep)
661 return 0;
662
663 r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
664 if (r)
665 goto err;
666
667 of_node_put(ep);
668 return 0;
669
670err:
671 of_node_put(ep);
672 return r;
673}
674
675/* HDMI HW IP initialisation */
676static int omapdss_hdmihw_probe(struct platform_device *pdev)
677{
678 int r;
679 int irq;
680
681 hdmi.pdev = pdev;
Jyri Sarha945514b2014-06-27 16:47:00 +0300682 dev_set_drvdata(&pdev->dev, &hdmi);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200683
684 mutex_init(&hdmi.lock);
685
686 if (pdev->dev.of_node) {
687 r = hdmi_probe_of(pdev);
688 if (r)
689 return r;
690 }
691
692 r = hdmi_wp_init(pdev, &hdmi.wp);
693 if (r)
694 return r;
695
Tomi Valkeinen03aafa22014-10-16 15:31:38 +0300696 r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200697 if (r)
698 return r;
699
700 r = hdmi_phy_init(pdev, &hdmi.phy);
701 if (r)
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300702 goto err;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200703
704 r = hdmi5_core_init(pdev, &hdmi.core);
705 if (r)
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300706 goto err;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200707
708 irq = platform_get_irq(pdev, 0);
709 if (irq < 0) {
710 DSSERR("platform_get_irq failed\n");
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300711 r = -ENODEV;
712 goto err;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200713 }
714
715 r = devm_request_threaded_irq(&pdev->dev, irq,
716 NULL, hdmi_irq_handler,
717 IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
718 if (r) {
719 DSSERR("HDMI IRQ request failed\n");
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300720 goto err;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200721 }
722
723 pm_runtime_enable(&pdev->dev);
724
725 hdmi_init_output(pdev);
726
727 dss_debugfs_create_file("hdmi", hdmi_dump_regs);
728
729 return 0;
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300730err:
731 hdmi_pll_uninit(&hdmi.pll);
732 return r;
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200733}
734
735static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
736{
737 hdmi_uninit_output(pdev);
738
Tomi Valkeinenc84c3a52014-10-22 15:02:17 +0300739 hdmi_pll_uninit(&hdmi.pll);
740
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200741 pm_runtime_disable(&pdev->dev);
742
743 return 0;
744}
745
746static int hdmi_runtime_suspend(struct device *dev)
747{
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200748 dispc_runtime_put();
749
750 return 0;
751}
752
753static int hdmi_runtime_resume(struct device *dev)
754{
755 int r;
756
757 r = dispc_runtime_get();
758 if (r < 0)
759 return r;
760
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200761 return 0;
762}
763
764static const struct dev_pm_ops hdmi_pm_ops = {
765 .runtime_suspend = hdmi_runtime_suspend,
766 .runtime_resume = hdmi_runtime_resume,
767};
768
769static const struct of_device_id hdmi_of_match[] = {
770 { .compatible = "ti,omap5-hdmi", },
771 {},
772};
773
774static struct platform_driver omapdss_hdmihw_driver = {
775 .probe = omapdss_hdmihw_probe,
776 .remove = __exit_p(omapdss_hdmihw_remove),
777 .driver = {
778 .name = "omapdss_hdmi5",
779 .owner = THIS_MODULE,
780 .pm = &hdmi_pm_ops,
781 .of_match_table = hdmi_of_match,
Tomi Valkeinen422ccbd2014-10-16 09:54:25 +0300782 .suppress_bind_attrs = true,
Tomi Valkeinenf5bab222014-03-13 12:44:14 +0200783 },
784};
785
786int __init hdmi5_init_platform_driver(void)
787{
788 return platform_driver_register(&omapdss_hdmihw_driver);
789}
790
791void __exit hdmi5_uninit_platform_driver(void)
792{
793 platform_driver_unregister(&omapdss_hdmihw_driver);
794}