]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/video/fbdev/omap2/dss/hdmi5.c
OMAPDSS: hdmi5: Remove callbacks for the old ASoC DAI driver
[mirror_ubuntu-artful-kernel.git] / drivers / video / fbdev / omap2 / dss / hdmi5.c
CommitLineData
f5bab222
TV
1/*
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
945514b5 46static struct omap_hdmi hdmi;
f5bab222
TV
47
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;
c84c3a5b 184 struct dss_pll_clock_info hdmi_cinfo = { 0 };
f5bab222
TV
185
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
c84c3a5b 194 hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo);
f5bab222
TV
195
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
c84c3a5b 201 r = dss_pll_enable(&hdmi.pll.pll);
f5bab222 202 if (r) {
c2fbd061 203 DSSERR("Failed to enable PLL\n");
f5bab222
TV
204 goto err_pll_enable;
205 }
206
c84c3a5b 207 r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
c2fbd061
TV
208 if (r) {
209 DSSERR("Failed to configure PLL\n");
210 goto err_pll_cfg;
211 }
212
c84c3a5b
TV
213 r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
214 hdmi_cinfo.clkout[0]);
f5bab222
TV
215 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:
c2fbd061 251err_pll_cfg:
c84c3a5b 252 dss_pll_disable(&hdmi.pll.pll);
f5bab222
TV
253err_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
c84c3a5b 270 dss_pll_disable(&hdmi.pll.pll);
f5bab222
TV
271
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
31dd0f4b
TV
280 /* TODO: proper interlace support */
281 if (timings->interlace)
282 return -EINVAL;
283
f5bab222
TV
284 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{
f5bab222
TV
293 mutex_lock(&hdmi.lock);
294
769dcb11 295 hdmi.cfg.timings = *timings;
f5bab222 296
769dcb11 297 dispc_set_tv_pclk(timings->pixelclock);
f5bab222
TV
298
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{
769dcb11 305 *timings = hdmi.cfg.timings;
f5bab222
TV
306}
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
f5bab222
TV
423static 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
769dcb11
TV
488static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
489 const struct hdmi_avi_infoframe *avi)
490{
491 hdmi.cfg.infoframe = *avi;
492 return 0;
493}
494
495static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
496 bool hdmi_mode)
497{
498 hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
499 return 0;
500}
501
f5bab222
TV
502static const struct omapdss_hdmi_ops hdmi_ops = {
503 .connect = hdmi_connect,
504 .disconnect = hdmi_disconnect,
505
506 .enable = hdmi_display_enable,
507 .disable = hdmi_display_disable,
508
509 .check_timings = hdmi_display_check_timing,
510 .set_timings = hdmi_display_set_timing,
511 .get_timings = hdmi_display_get_timings,
512
513 .read_edid = hdmi_read_edid,
769dcb11
TV
514 .set_infoframe = hdmi_set_infoframe,
515 .set_hdmi_mode = hdmi_set_hdmi_mode,
f5bab222
TV
516};
517
518static void hdmi_init_output(struct platform_device *pdev)
519{
520 struct omap_dss_device *out = &hdmi.output;
521
522 out->dev = &pdev->dev;
523 out->id = OMAP_DSS_OUTPUT_HDMI;
524 out->output_type = OMAP_DISPLAY_TYPE_HDMI;
525 out->name = "hdmi.0";
526 out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
527 out->ops.hdmi = &hdmi_ops;
528 out->owner = THIS_MODULE;
529
530 omapdss_register_output(out);
531}
532
533static void __exit hdmi_uninit_output(struct platform_device *pdev)
534{
535 struct omap_dss_device *out = &hdmi.output;
536
537 omapdss_unregister_output(out);
538}
539
540static int hdmi_probe_of(struct platform_device *pdev)
541{
542 struct device_node *node = pdev->dev.of_node;
543 struct device_node *ep;
544 int r;
545
546 ep = omapdss_of_get_first_endpoint(node);
547 if (!ep)
548 return 0;
549
550 r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
551 if (r)
552 goto err;
553
554 of_node_put(ep);
555 return 0;
556
557err:
558 of_node_put(ep);
559 return r;
560}
561
562/* HDMI HW IP initialisation */
563static int omapdss_hdmihw_probe(struct platform_device *pdev)
564{
565 int r;
566 int irq;
567
568 hdmi.pdev = pdev;
945514b5 569 dev_set_drvdata(&pdev->dev, &hdmi);
f5bab222
TV
570
571 mutex_init(&hdmi.lock);
572
573 if (pdev->dev.of_node) {
574 r = hdmi_probe_of(pdev);
575 if (r)
576 return r;
577 }
578
579 r = hdmi_wp_init(pdev, &hdmi.wp);
580 if (r)
581 return r;
582
03aafa2c 583 r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
f5bab222
TV
584 if (r)
585 return r;
586
587 r = hdmi_phy_init(pdev, &hdmi.phy);
588 if (r)
c84c3a5b 589 goto err;
f5bab222
TV
590
591 r = hdmi5_core_init(pdev, &hdmi.core);
592 if (r)
c84c3a5b 593 goto err;
f5bab222
TV
594
595 irq = platform_get_irq(pdev, 0);
596 if (irq < 0) {
597 DSSERR("platform_get_irq failed\n");
c84c3a5b
TV
598 r = -ENODEV;
599 goto err;
f5bab222
TV
600 }
601
602 r = devm_request_threaded_irq(&pdev->dev, irq,
603 NULL, hdmi_irq_handler,
604 IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
605 if (r) {
606 DSSERR("HDMI IRQ request failed\n");
c84c3a5b 607 goto err;
f5bab222
TV
608 }
609
610 pm_runtime_enable(&pdev->dev);
611
612 hdmi_init_output(pdev);
613
614 dss_debugfs_create_file("hdmi", hdmi_dump_regs);
615
616 return 0;
c84c3a5b
TV
617err:
618 hdmi_pll_uninit(&hdmi.pll);
619 return r;
f5bab222
TV
620}
621
622static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
623{
624 hdmi_uninit_output(pdev);
625
c84c3a5b
TV
626 hdmi_pll_uninit(&hdmi.pll);
627
f5bab222
TV
628 pm_runtime_disable(&pdev->dev);
629
630 return 0;
631}
632
633static int hdmi_runtime_suspend(struct device *dev)
634{
f5bab222
TV
635 dispc_runtime_put();
636
637 return 0;
638}
639
640static int hdmi_runtime_resume(struct device *dev)
641{
642 int r;
643
644 r = dispc_runtime_get();
645 if (r < 0)
646 return r;
647
f5bab222
TV
648 return 0;
649}
650
651static const struct dev_pm_ops hdmi_pm_ops = {
652 .runtime_suspend = hdmi_runtime_suspend,
653 .runtime_resume = hdmi_runtime_resume,
654};
655
656static const struct of_device_id hdmi_of_match[] = {
657 { .compatible = "ti,omap5-hdmi", },
658 {},
659};
660
661static struct platform_driver omapdss_hdmihw_driver = {
662 .probe = omapdss_hdmihw_probe,
663 .remove = __exit_p(omapdss_hdmihw_remove),
664 .driver = {
665 .name = "omapdss_hdmi5",
666 .owner = THIS_MODULE,
667 .pm = &hdmi_pm_ops,
668 .of_match_table = hdmi_of_match,
422ccbd5 669 .suppress_bind_attrs = true,
f5bab222
TV
670 },
671};
672
673int __init hdmi5_init_platform_driver(void)
674{
675 return platform_driver_register(&omapdss_hdmihw_driver);
676}
677
678void __exit hdmi5_uninit_platform_driver(void)
679{
680 platform_driver_unregister(&omapdss_hdmihw_driver);
681}