1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f76ee892STomi Valkeinen /*
3f76ee892STomi Valkeinen * HDMI driver for OMAP5
4f76ee892STomi Valkeinen *
5f76ee892STomi Valkeinen * Copyright (C) 2014 Texas Instruments Incorporated
6f76ee892STomi Valkeinen *
7f76ee892STomi Valkeinen * Authors:
8f76ee892STomi Valkeinen * Yong Zhi
9f76ee892STomi Valkeinen * Mythri pk
10f76ee892STomi Valkeinen * Archit Taneja <archit@ti.com>
11f76ee892STomi Valkeinen * Tomi Valkeinen <tomi.valkeinen@ti.com>
12f76ee892STomi Valkeinen */
13f76ee892STomi Valkeinen
14f76ee892STomi Valkeinen #define DSS_SUBSYS_NAME "HDMI"
15f76ee892STomi Valkeinen
16f76ee892STomi Valkeinen #include <linux/kernel.h>
17f76ee892STomi Valkeinen #include <linux/module.h>
18f76ee892STomi Valkeinen #include <linux/err.h>
19f76ee892STomi Valkeinen #include <linux/io.h>
20f76ee892STomi Valkeinen #include <linux/interrupt.h>
21f76ee892STomi Valkeinen #include <linux/mutex.h>
22f76ee892STomi Valkeinen #include <linux/delay.h>
23f76ee892STomi Valkeinen #include <linux/string.h>
24f76ee892STomi Valkeinen #include <linux/platform_device.h>
25f76ee892STomi Valkeinen #include <linux/pm_runtime.h>
26f76ee892STomi Valkeinen #include <linux/clk.h>
2785806f6dSLinus Walleij #include <linux/of.h>
28ada5caa4SKuninori Morimoto #include <linux/of_graph.h>
29f76ee892STomi Valkeinen #include <linux/regulator/consumer.h>
30f76ee892STomi Valkeinen #include <linux/component.h>
3162d9e44eSPeter Ujfalusi #include <video/omapfb_dss.h>
32f76ee892STomi Valkeinen #include <sound/omap-hdmi-audio.h>
33f76ee892STomi Valkeinen
34f76ee892STomi Valkeinen #include "hdmi5_core.h"
35f76ee892STomi Valkeinen #include "dss.h"
36f76ee892STomi Valkeinen #include "dss_features.h"
37f76ee892STomi Valkeinen
38f76ee892STomi Valkeinen static struct omap_hdmi hdmi;
39f76ee892STomi Valkeinen
hdmi_runtime_get(void)40f76ee892STomi Valkeinen static int hdmi_runtime_get(void)
41f76ee892STomi Valkeinen {
42f76ee892STomi Valkeinen int r;
43f76ee892STomi Valkeinen
44f76ee892STomi Valkeinen DSSDBG("hdmi_runtime_get\n");
45f76ee892STomi Valkeinen
46b0e07060SZhang Qilong r = pm_runtime_resume_and_get(&hdmi.pdev->dev);
47b0e07060SZhang Qilong if (WARN_ON(r < 0))
48f76ee892STomi Valkeinen return r;
49f76ee892STomi Valkeinen
50f76ee892STomi Valkeinen return 0;
51f76ee892STomi Valkeinen }
52f76ee892STomi Valkeinen
hdmi_runtime_put(void)53f76ee892STomi Valkeinen static void hdmi_runtime_put(void)
54f76ee892STomi Valkeinen {
55f76ee892STomi Valkeinen int r;
56f76ee892STomi Valkeinen
57f76ee892STomi Valkeinen DSSDBG("hdmi_runtime_put\n");
58f76ee892STomi Valkeinen
59f76ee892STomi Valkeinen r = pm_runtime_put_sync(&hdmi.pdev->dev);
60f76ee892STomi Valkeinen WARN_ON(r < 0 && r != -ENOSYS);
61f76ee892STomi Valkeinen }
62f76ee892STomi Valkeinen
hdmi_irq_handler(int irq,void * data)63f76ee892STomi Valkeinen static irqreturn_t hdmi_irq_handler(int irq, void *data)
64f76ee892STomi Valkeinen {
65f76ee892STomi Valkeinen struct hdmi_wp_data *wp = data;
66f76ee892STomi Valkeinen u32 irqstatus;
67f76ee892STomi Valkeinen
68f76ee892STomi Valkeinen irqstatus = hdmi_wp_get_irqstatus(wp);
69f76ee892STomi Valkeinen hdmi_wp_set_irqstatus(wp, irqstatus);
70f76ee892STomi Valkeinen
71f76ee892STomi Valkeinen if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
72f76ee892STomi Valkeinen irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
73f76ee892STomi Valkeinen u32 v;
74f76ee892STomi Valkeinen /*
75f76ee892STomi Valkeinen * If we get both connect and disconnect interrupts at the same
76f76ee892STomi Valkeinen * time, turn off the PHY, clear interrupts, and restart, which
77f76ee892STomi Valkeinen * raises connect interrupt if a cable is connected, or nothing
78f76ee892STomi Valkeinen * if cable is not connected.
79f76ee892STomi Valkeinen */
80f76ee892STomi Valkeinen
81f76ee892STomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
82f76ee892STomi Valkeinen
83f76ee892STomi Valkeinen /*
84f76ee892STomi Valkeinen * We always get bogus CONNECT & DISCONNECT interrupts when
85f76ee892STomi Valkeinen * setting the PHY to LDOON. To ignore those, we force the RXDET
86f76ee892STomi Valkeinen * line to 0 until the PHY power state has been changed.
87f76ee892STomi Valkeinen */
88f76ee892STomi Valkeinen v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL);
89f76ee892STomi Valkeinen v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */
90f76ee892STomi Valkeinen v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */
91f76ee892STomi Valkeinen hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v);
92f76ee892STomi Valkeinen
93f76ee892STomi Valkeinen hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
94f76ee892STomi Valkeinen HDMI_IRQ_LINK_DISCONNECT);
95f76ee892STomi Valkeinen
96f76ee892STomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
97f76ee892STomi Valkeinen
98f76ee892STomi Valkeinen REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15);
99f76ee892STomi Valkeinen
100f76ee892STomi Valkeinen } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
101f76ee892STomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
102f76ee892STomi Valkeinen } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
103f76ee892STomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
104f76ee892STomi Valkeinen }
105f76ee892STomi Valkeinen
106f76ee892STomi Valkeinen return IRQ_HANDLED;
107f76ee892STomi Valkeinen }
108f76ee892STomi Valkeinen
hdmi_init_regulator(void)109f76ee892STomi Valkeinen static int hdmi_init_regulator(void)
110f76ee892STomi Valkeinen {
111f76ee892STomi Valkeinen struct regulator *reg;
112f76ee892STomi Valkeinen
113f76ee892STomi Valkeinen if (hdmi.vdda_reg != NULL)
114f76ee892STomi Valkeinen return 0;
115f76ee892STomi Valkeinen
116f76ee892STomi Valkeinen reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
117f76ee892STomi Valkeinen if (IS_ERR(reg)) {
118f76ee892STomi Valkeinen DSSERR("can't get VDDA regulator\n");
119f76ee892STomi Valkeinen return PTR_ERR(reg);
120f76ee892STomi Valkeinen }
121f76ee892STomi Valkeinen
122f76ee892STomi Valkeinen hdmi.vdda_reg = reg;
123f76ee892STomi Valkeinen
124f76ee892STomi Valkeinen return 0;
125f76ee892STomi Valkeinen }
126f76ee892STomi Valkeinen
hdmi_power_on_core(struct omap_dss_device * dssdev)127f76ee892STomi Valkeinen static int hdmi_power_on_core(struct omap_dss_device *dssdev)
128f76ee892STomi Valkeinen {
129f76ee892STomi Valkeinen int r;
130f76ee892STomi Valkeinen
131f76ee892STomi Valkeinen r = regulator_enable(hdmi.vdda_reg);
132f76ee892STomi Valkeinen if (r)
133f76ee892STomi Valkeinen return r;
134f76ee892STomi Valkeinen
135f76ee892STomi Valkeinen r = hdmi_runtime_get();
136f76ee892STomi Valkeinen if (r)
137f76ee892STomi Valkeinen goto err_runtime_get;
138f76ee892STomi Valkeinen
139f76ee892STomi Valkeinen /* Make selection of HDMI in DSS */
140f76ee892STomi Valkeinen dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
141f76ee892STomi Valkeinen
142f76ee892STomi Valkeinen hdmi.core_enabled = true;
143f76ee892STomi Valkeinen
144f76ee892STomi Valkeinen return 0;
145f76ee892STomi Valkeinen
146f76ee892STomi Valkeinen err_runtime_get:
147f76ee892STomi Valkeinen regulator_disable(hdmi.vdda_reg);
148f76ee892STomi Valkeinen
149f76ee892STomi Valkeinen return r;
150f76ee892STomi Valkeinen }
151f76ee892STomi Valkeinen
hdmi_power_off_core(struct omap_dss_device * dssdev)152f76ee892STomi Valkeinen static void hdmi_power_off_core(struct omap_dss_device *dssdev)
153f76ee892STomi Valkeinen {
154f76ee892STomi Valkeinen hdmi.core_enabled = false;
155f76ee892STomi Valkeinen
156f76ee892STomi Valkeinen hdmi_runtime_put();
157f76ee892STomi Valkeinen regulator_disable(hdmi.vdda_reg);
158f76ee892STomi Valkeinen }
159f76ee892STomi Valkeinen
hdmi_power_on_full(struct omap_dss_device * dssdev)160f76ee892STomi Valkeinen static int hdmi_power_on_full(struct omap_dss_device *dssdev)
161f76ee892STomi Valkeinen {
162f76ee892STomi Valkeinen int r;
163f76ee892STomi Valkeinen struct omap_video_timings *p;
164f76ee892STomi Valkeinen struct omap_overlay_manager *mgr = hdmi.output.manager;
165f76ee892STomi Valkeinen struct dss_pll_clock_info hdmi_cinfo = { 0 };
166f76ee892STomi Valkeinen
167f76ee892STomi Valkeinen r = hdmi_power_on_core(dssdev);
168f76ee892STomi Valkeinen if (r)
169f76ee892STomi Valkeinen return r;
170f76ee892STomi Valkeinen
171f76ee892STomi Valkeinen p = &hdmi.cfg.timings;
172f76ee892STomi Valkeinen
173f76ee892STomi Valkeinen DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
174f76ee892STomi Valkeinen
175f76ee892STomi Valkeinen hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo);
176f76ee892STomi Valkeinen
177f76ee892STomi Valkeinen /* disable and clear irqs */
178f76ee892STomi Valkeinen hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
179f76ee892STomi Valkeinen hdmi_wp_set_irqstatus(&hdmi.wp,
180f76ee892STomi Valkeinen hdmi_wp_get_irqstatus(&hdmi.wp));
181f76ee892STomi Valkeinen
182f76ee892STomi Valkeinen r = dss_pll_enable(&hdmi.pll.pll);
183f76ee892STomi Valkeinen if (r) {
184f76ee892STomi Valkeinen DSSERR("Failed to enable PLL\n");
185f76ee892STomi Valkeinen goto err_pll_enable;
186f76ee892STomi Valkeinen }
187f76ee892STomi Valkeinen
188f76ee892STomi Valkeinen r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
189f76ee892STomi Valkeinen if (r) {
190f76ee892STomi Valkeinen DSSERR("Failed to configure PLL\n");
191f76ee892STomi Valkeinen goto err_pll_cfg;
192f76ee892STomi Valkeinen }
193f76ee892STomi Valkeinen
194f76ee892STomi Valkeinen r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
195f76ee892STomi Valkeinen hdmi_cinfo.clkout[0]);
196f76ee892STomi Valkeinen if (r) {
197f76ee892STomi Valkeinen DSSDBG("Failed to start PHY\n");
198f76ee892STomi Valkeinen goto err_phy_cfg;
199f76ee892STomi Valkeinen }
200f76ee892STomi Valkeinen
201f76ee892STomi Valkeinen r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON);
202f76ee892STomi Valkeinen if (r)
203f76ee892STomi Valkeinen goto err_phy_pwr;
204f76ee892STomi Valkeinen
205f76ee892STomi Valkeinen hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
206f76ee892STomi Valkeinen
207f76ee892STomi Valkeinen /* bypass TV gamma table */
208f76ee892STomi Valkeinen dispc_enable_gamma_table(0);
209f76ee892STomi Valkeinen
210f76ee892STomi Valkeinen /* tv size */
211f76ee892STomi Valkeinen dss_mgr_set_timings(mgr, p);
212f76ee892STomi Valkeinen
213f76ee892STomi Valkeinen r = hdmi_wp_video_start(&hdmi.wp);
214f76ee892STomi Valkeinen if (r)
215f76ee892STomi Valkeinen goto err_vid_enable;
216f76ee892STomi Valkeinen
217f76ee892STomi Valkeinen r = dss_mgr_enable(mgr);
218f76ee892STomi Valkeinen if (r)
219f76ee892STomi Valkeinen goto err_mgr_enable;
220f76ee892STomi Valkeinen
221f76ee892STomi Valkeinen hdmi_wp_set_irqenable(&hdmi.wp,
222f76ee892STomi Valkeinen HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
223f76ee892STomi Valkeinen
224f76ee892STomi Valkeinen return 0;
225f76ee892STomi Valkeinen
226f76ee892STomi Valkeinen err_mgr_enable:
227f76ee892STomi Valkeinen hdmi_wp_video_stop(&hdmi.wp);
228f76ee892STomi Valkeinen err_vid_enable:
229f76ee892STomi Valkeinen hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
230f76ee892STomi Valkeinen err_phy_pwr:
231f76ee892STomi Valkeinen err_phy_cfg:
232f76ee892STomi Valkeinen err_pll_cfg:
233f76ee892STomi Valkeinen dss_pll_disable(&hdmi.pll.pll);
234f76ee892STomi Valkeinen err_pll_enable:
235f76ee892STomi Valkeinen hdmi_power_off_core(dssdev);
236f76ee892STomi Valkeinen return -EIO;
237f76ee892STomi Valkeinen }
238f76ee892STomi Valkeinen
hdmi_power_off_full(struct omap_dss_device * dssdev)239f76ee892STomi Valkeinen static void hdmi_power_off_full(struct omap_dss_device *dssdev)
240f76ee892STomi Valkeinen {
241f76ee892STomi Valkeinen struct omap_overlay_manager *mgr = hdmi.output.manager;
242f76ee892STomi Valkeinen
243f76ee892STomi Valkeinen hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
244f76ee892STomi Valkeinen
245f76ee892STomi Valkeinen dss_mgr_disable(mgr);
246f76ee892STomi Valkeinen
247f76ee892STomi Valkeinen hdmi_wp_video_stop(&hdmi.wp);
248f76ee892STomi Valkeinen
249f76ee892STomi Valkeinen hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
250f76ee892STomi Valkeinen
251f76ee892STomi Valkeinen dss_pll_disable(&hdmi.pll.pll);
252f76ee892STomi Valkeinen
253f76ee892STomi Valkeinen hdmi_power_off_core(dssdev);
254f76ee892STomi Valkeinen }
255f76ee892STomi Valkeinen
hdmi_display_check_timing(struct omap_dss_device * dssdev,struct omap_video_timings * timings)256f76ee892STomi Valkeinen static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
257f76ee892STomi Valkeinen struct omap_video_timings *timings)
258f76ee892STomi Valkeinen {
259f76ee892STomi Valkeinen struct omap_dss_device *out = &hdmi.output;
260f76ee892STomi Valkeinen
261f76ee892STomi Valkeinen /* TODO: proper interlace support */
262f76ee892STomi Valkeinen if (timings->interlace)
263f76ee892STomi Valkeinen return -EINVAL;
264f76ee892STomi Valkeinen
265f76ee892STomi Valkeinen if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
266f76ee892STomi Valkeinen return -EINVAL;
267f76ee892STomi Valkeinen
268f76ee892STomi Valkeinen return 0;
269f76ee892STomi Valkeinen }
270f76ee892STomi Valkeinen
hdmi_display_set_timing(struct omap_dss_device * dssdev,struct omap_video_timings * timings)271f76ee892STomi Valkeinen static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
272f76ee892STomi Valkeinen struct omap_video_timings *timings)
273f76ee892STomi Valkeinen {
274f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
275f76ee892STomi Valkeinen
276f76ee892STomi Valkeinen hdmi.cfg.timings = *timings;
277f76ee892STomi Valkeinen
278f76ee892STomi Valkeinen dispc_set_tv_pclk(timings->pixelclock);
279f76ee892STomi Valkeinen
280f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
281f76ee892STomi Valkeinen }
282f76ee892STomi Valkeinen
hdmi_display_get_timings(struct omap_dss_device * dssdev,struct omap_video_timings * timings)283f76ee892STomi Valkeinen static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
284f76ee892STomi Valkeinen struct omap_video_timings *timings)
285f76ee892STomi Valkeinen {
286f76ee892STomi Valkeinen *timings = hdmi.cfg.timings;
287f76ee892STomi Valkeinen }
288f76ee892STomi Valkeinen
hdmi_dump_regs(struct seq_file * s)289f76ee892STomi Valkeinen static void hdmi_dump_regs(struct seq_file *s)
290f76ee892STomi Valkeinen {
291f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
292f76ee892STomi Valkeinen
293f76ee892STomi Valkeinen if (hdmi_runtime_get()) {
294f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
295f76ee892STomi Valkeinen return;
296f76ee892STomi Valkeinen }
297f76ee892STomi Valkeinen
298f76ee892STomi Valkeinen hdmi_wp_dump(&hdmi.wp, s);
299f76ee892STomi Valkeinen hdmi_pll_dump(&hdmi.pll, s);
300f76ee892STomi Valkeinen hdmi_phy_dump(&hdmi.phy, s);
301f76ee892STomi Valkeinen hdmi5_core_dump(&hdmi.core, s);
302f76ee892STomi Valkeinen
303f76ee892STomi Valkeinen hdmi_runtime_put();
304f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
305f76ee892STomi Valkeinen }
306f76ee892STomi Valkeinen
read_edid(u8 * buf,int len)307f76ee892STomi Valkeinen static int read_edid(u8 *buf, int len)
308f76ee892STomi Valkeinen {
309f76ee892STomi Valkeinen int r;
310f76ee892STomi Valkeinen int idlemode;
311f76ee892STomi Valkeinen
312f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
313f76ee892STomi Valkeinen
314f76ee892STomi Valkeinen r = hdmi_runtime_get();
315f76ee892STomi Valkeinen BUG_ON(r);
316f76ee892STomi Valkeinen
317f76ee892STomi Valkeinen idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
318f76ee892STomi Valkeinen /* No-idle mode */
319f76ee892STomi Valkeinen REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
320f76ee892STomi Valkeinen
321f76ee892STomi Valkeinen r = hdmi5_read_edid(&hdmi.core, buf, len);
322f76ee892STomi Valkeinen
323f76ee892STomi Valkeinen REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
324f76ee892STomi Valkeinen
325f76ee892STomi Valkeinen hdmi_runtime_put();
326f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
327f76ee892STomi Valkeinen
328f76ee892STomi Valkeinen return r;
329f76ee892STomi Valkeinen }
330f76ee892STomi Valkeinen
hdmi_start_audio_stream(struct omap_hdmi * hd)331f76ee892STomi Valkeinen static void hdmi_start_audio_stream(struct omap_hdmi *hd)
332f76ee892STomi Valkeinen {
333f76ee892STomi Valkeinen REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
334f76ee892STomi Valkeinen hdmi_wp_audio_enable(&hd->wp, true);
335f76ee892STomi Valkeinen hdmi_wp_audio_core_req_enable(&hd->wp, true);
336f76ee892STomi Valkeinen }
337f76ee892STomi Valkeinen
hdmi_stop_audio_stream(struct omap_hdmi * hd)338f76ee892STomi Valkeinen static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
339f76ee892STomi Valkeinen {
340f76ee892STomi Valkeinen hdmi_wp_audio_core_req_enable(&hd->wp, false);
341f76ee892STomi Valkeinen hdmi_wp_audio_enable(&hd->wp, false);
342f76ee892STomi Valkeinen REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
343f76ee892STomi Valkeinen }
344f76ee892STomi Valkeinen
hdmi_display_enable(struct omap_dss_device * dssdev)345f76ee892STomi Valkeinen static int hdmi_display_enable(struct omap_dss_device *dssdev)
346f76ee892STomi Valkeinen {
347f76ee892STomi Valkeinen struct omap_dss_device *out = &hdmi.output;
348f76ee892STomi Valkeinen unsigned long flags;
349f76ee892STomi Valkeinen int r = 0;
350f76ee892STomi Valkeinen
351f76ee892STomi Valkeinen DSSDBG("ENTER hdmi_display_enable\n");
352f76ee892STomi Valkeinen
353f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
354f76ee892STomi Valkeinen
355f76ee892STomi Valkeinen if (out->manager == NULL) {
356f76ee892STomi Valkeinen DSSERR("failed to enable display: no output/manager\n");
357f76ee892STomi Valkeinen r = -ENODEV;
358f76ee892STomi Valkeinen goto err0;
359f76ee892STomi Valkeinen }
360f76ee892STomi Valkeinen
361f76ee892STomi Valkeinen r = hdmi_power_on_full(dssdev);
362f76ee892STomi Valkeinen if (r) {
363f76ee892STomi Valkeinen DSSERR("failed to power on device\n");
364f76ee892STomi Valkeinen goto err0;
365f76ee892STomi Valkeinen }
366f76ee892STomi Valkeinen
367f76ee892STomi Valkeinen if (hdmi.audio_configured) {
368f76ee892STomi Valkeinen r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
369f76ee892STomi Valkeinen hdmi.cfg.timings.pixelclock);
370f76ee892STomi Valkeinen if (r) {
371f76ee892STomi Valkeinen DSSERR("Error restoring audio configuration: %d", r);
372f76ee892STomi Valkeinen hdmi.audio_abort_cb(&hdmi.pdev->dev);
373f76ee892STomi Valkeinen hdmi.audio_configured = false;
374f76ee892STomi Valkeinen }
375f76ee892STomi Valkeinen }
376f76ee892STomi Valkeinen
377f76ee892STomi Valkeinen spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
378f76ee892STomi Valkeinen if (hdmi.audio_configured && hdmi.audio_playing)
379f76ee892STomi Valkeinen hdmi_start_audio_stream(&hdmi);
380f76ee892STomi Valkeinen hdmi.display_enabled = true;
381f76ee892STomi Valkeinen spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
382f76ee892STomi Valkeinen
383f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
384f76ee892STomi Valkeinen return 0;
385f76ee892STomi Valkeinen
386f76ee892STomi Valkeinen err0:
387f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
388f76ee892STomi Valkeinen return r;
389f76ee892STomi Valkeinen }
390f76ee892STomi Valkeinen
hdmi_display_disable(struct omap_dss_device * dssdev)391f76ee892STomi Valkeinen static void hdmi_display_disable(struct omap_dss_device *dssdev)
392f76ee892STomi Valkeinen {
393f76ee892STomi Valkeinen unsigned long flags;
394f76ee892STomi Valkeinen
395f76ee892STomi Valkeinen DSSDBG("Enter hdmi_display_disable\n");
396f76ee892STomi Valkeinen
397f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
398f76ee892STomi Valkeinen
399f76ee892STomi Valkeinen spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
400f76ee892STomi Valkeinen hdmi_stop_audio_stream(&hdmi);
401f76ee892STomi Valkeinen hdmi.display_enabled = false;
402f76ee892STomi Valkeinen spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
403f76ee892STomi Valkeinen
404f76ee892STomi Valkeinen hdmi_power_off_full(dssdev);
405f76ee892STomi Valkeinen
406f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
407f76ee892STomi Valkeinen }
408f76ee892STomi Valkeinen
hdmi_core_enable(struct omap_dss_device * dssdev)409f76ee892STomi Valkeinen static int hdmi_core_enable(struct omap_dss_device *dssdev)
410f76ee892STomi Valkeinen {
411f76ee892STomi Valkeinen int r = 0;
412f76ee892STomi Valkeinen
413f76ee892STomi Valkeinen DSSDBG("ENTER omapdss_hdmi_core_enable\n");
414f76ee892STomi Valkeinen
415f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
416f76ee892STomi Valkeinen
417f76ee892STomi Valkeinen r = hdmi_power_on_core(dssdev);
418f76ee892STomi Valkeinen if (r) {
419f76ee892STomi Valkeinen DSSERR("failed to power on device\n");
420f76ee892STomi Valkeinen goto err0;
421f76ee892STomi Valkeinen }
422f76ee892STomi Valkeinen
423f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
424f76ee892STomi Valkeinen return 0;
425f76ee892STomi Valkeinen
426f76ee892STomi Valkeinen err0:
427f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
428f76ee892STomi Valkeinen return r;
429f76ee892STomi Valkeinen }
430f76ee892STomi Valkeinen
hdmi_core_disable(struct omap_dss_device * dssdev)431f76ee892STomi Valkeinen static void hdmi_core_disable(struct omap_dss_device *dssdev)
432f76ee892STomi Valkeinen {
433f76ee892STomi Valkeinen DSSDBG("Enter omapdss_hdmi_core_disable\n");
434f76ee892STomi Valkeinen
435f76ee892STomi Valkeinen mutex_lock(&hdmi.lock);
436f76ee892STomi Valkeinen
437f76ee892STomi Valkeinen hdmi_power_off_core(dssdev);
438f76ee892STomi Valkeinen
439f76ee892STomi Valkeinen mutex_unlock(&hdmi.lock);
440f76ee892STomi Valkeinen }
441f76ee892STomi Valkeinen
hdmi_connect(struct omap_dss_device * dssdev,struct omap_dss_device * dst)442f76ee892STomi Valkeinen static int hdmi_connect(struct omap_dss_device *dssdev,
443f76ee892STomi Valkeinen struct omap_dss_device *dst)
444f76ee892STomi Valkeinen {
445f76ee892STomi Valkeinen struct omap_overlay_manager *mgr;
446f76ee892STomi Valkeinen int r;
447f76ee892STomi Valkeinen
448f76ee892STomi Valkeinen r = hdmi_init_regulator();
449f76ee892STomi Valkeinen if (r)
450f76ee892STomi Valkeinen return r;
451f76ee892STomi Valkeinen
452f76ee892STomi Valkeinen mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
453f76ee892STomi Valkeinen if (!mgr)
454f76ee892STomi Valkeinen return -ENODEV;
455f76ee892STomi Valkeinen
456f76ee892STomi Valkeinen r = dss_mgr_connect(mgr, dssdev);
457f76ee892STomi Valkeinen if (r)
458f76ee892STomi Valkeinen return r;
459f76ee892STomi Valkeinen
460f76ee892STomi Valkeinen r = omapdss_output_set_device(dssdev, dst);
461f76ee892STomi Valkeinen if (r) {
462f76ee892STomi Valkeinen DSSERR("failed to connect output to new device: %s\n",
463f76ee892STomi Valkeinen dst->name);
464f76ee892STomi Valkeinen dss_mgr_disconnect(mgr, dssdev);
465f76ee892STomi Valkeinen return r;
466f76ee892STomi Valkeinen }
467f76ee892STomi Valkeinen
468f76ee892STomi Valkeinen return 0;
469f76ee892STomi Valkeinen }
470f76ee892STomi Valkeinen
hdmi_disconnect(struct omap_dss_device * dssdev,struct omap_dss_device * dst)471f76ee892STomi Valkeinen static void hdmi_disconnect(struct omap_dss_device *dssdev,
472f76ee892STomi Valkeinen struct omap_dss_device *dst)
473f76ee892STomi Valkeinen {
474f76ee892STomi Valkeinen WARN_ON(dst != dssdev->dst);
475f76ee892STomi Valkeinen
476f76ee892STomi Valkeinen if (dst != dssdev->dst)
477f76ee892STomi Valkeinen return;
478f76ee892STomi Valkeinen
479f76ee892STomi Valkeinen omapdss_output_unset_device(dssdev);
480f76ee892STomi Valkeinen
481f76ee892STomi Valkeinen if (dssdev->manager)
482f76ee892STomi Valkeinen dss_mgr_disconnect(dssdev->manager, dssdev);
483f76ee892STomi Valkeinen }
484f76ee892STomi Valkeinen
hdmi_read_edid(struct omap_dss_device * dssdev,u8 * edid,int len)485f76ee892STomi Valkeinen static int hdmi_read_edid(struct omap_dss_device *dssdev,
486f76ee892STomi Valkeinen u8 *edid, int len)
487f76ee892STomi Valkeinen {
488f76ee892STomi Valkeinen bool need_enable;
489f76ee892STomi Valkeinen int r;
490f76ee892STomi Valkeinen
491f76ee892STomi Valkeinen need_enable = hdmi.core_enabled == false;
492f76ee892STomi Valkeinen
493f76ee892STomi Valkeinen if (need_enable) {
494f76ee892STomi Valkeinen r = hdmi_core_enable(dssdev);
495f76ee892STomi Valkeinen if (r)
496f76ee892STomi Valkeinen return r;
497f76ee892STomi Valkeinen }
498f76ee892STomi Valkeinen
499f76ee892STomi Valkeinen r = read_edid(edid, len);
500f76ee892STomi Valkeinen
501f76ee892STomi Valkeinen if (need_enable)
502f76ee892STomi Valkeinen hdmi_core_disable(dssdev);
503f76ee892STomi Valkeinen
504f76ee892STomi Valkeinen return r;
505f76ee892STomi Valkeinen }
506f76ee892STomi Valkeinen
hdmi_set_infoframe(struct omap_dss_device * dssdev,const struct hdmi_avi_infoframe * avi)507f76ee892STomi Valkeinen static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
508f76ee892STomi Valkeinen const struct hdmi_avi_infoframe *avi)
509f76ee892STomi Valkeinen {
510f76ee892STomi Valkeinen hdmi.cfg.infoframe = *avi;
511f76ee892STomi Valkeinen return 0;
512f76ee892STomi Valkeinen }
513f76ee892STomi Valkeinen
hdmi_set_hdmi_mode(struct omap_dss_device * dssdev,bool hdmi_mode)514f76ee892STomi Valkeinen static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
515f76ee892STomi Valkeinen bool hdmi_mode)
516f76ee892STomi Valkeinen {
517f76ee892STomi Valkeinen hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
518f76ee892STomi Valkeinen return 0;
519f76ee892STomi Valkeinen }
520f76ee892STomi Valkeinen
521f76ee892STomi Valkeinen static const struct omapdss_hdmi_ops hdmi_ops = {
522f76ee892STomi Valkeinen .connect = hdmi_connect,
523f76ee892STomi Valkeinen .disconnect = hdmi_disconnect,
524f76ee892STomi Valkeinen
525f76ee892STomi Valkeinen .enable = hdmi_display_enable,
526f76ee892STomi Valkeinen .disable = hdmi_display_disable,
527f76ee892STomi Valkeinen
528f76ee892STomi Valkeinen .check_timings = hdmi_display_check_timing,
529f76ee892STomi Valkeinen .set_timings = hdmi_display_set_timing,
530f76ee892STomi Valkeinen .get_timings = hdmi_display_get_timings,
531f76ee892STomi Valkeinen
532f76ee892STomi Valkeinen .read_edid = hdmi_read_edid,
533f76ee892STomi Valkeinen .set_infoframe = hdmi_set_infoframe,
534f76ee892STomi Valkeinen .set_hdmi_mode = hdmi_set_hdmi_mode,
535f76ee892STomi Valkeinen };
536f76ee892STomi Valkeinen
hdmi_init_output(struct platform_device * pdev)537f76ee892STomi Valkeinen static void hdmi_init_output(struct platform_device *pdev)
538f76ee892STomi Valkeinen {
539f76ee892STomi Valkeinen struct omap_dss_device *out = &hdmi.output;
540f76ee892STomi Valkeinen
541f76ee892STomi Valkeinen out->dev = &pdev->dev;
542f76ee892STomi Valkeinen out->id = OMAP_DSS_OUTPUT_HDMI;
543f76ee892STomi Valkeinen out->output_type = OMAP_DISPLAY_TYPE_HDMI;
544f76ee892STomi Valkeinen out->name = "hdmi.0";
545f76ee892STomi Valkeinen out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
546f76ee892STomi Valkeinen out->ops.hdmi = &hdmi_ops;
547f76ee892STomi Valkeinen out->owner = THIS_MODULE;
548f76ee892STomi Valkeinen
549f76ee892STomi Valkeinen omapdss_register_output(out);
550f76ee892STomi Valkeinen }
551f76ee892STomi Valkeinen
hdmi_uninit_output(struct platform_device * pdev)552f76ee892STomi Valkeinen static void hdmi_uninit_output(struct platform_device *pdev)
553f76ee892STomi Valkeinen {
554f76ee892STomi Valkeinen struct omap_dss_device *out = &hdmi.output;
555f76ee892STomi Valkeinen
556f76ee892STomi Valkeinen omapdss_unregister_output(out);
557f76ee892STomi Valkeinen }
558f76ee892STomi Valkeinen
hdmi_probe_of(struct platform_device * pdev)559f76ee892STomi Valkeinen static int hdmi_probe_of(struct platform_device *pdev)
560f76ee892STomi Valkeinen {
561f76ee892STomi Valkeinen struct device_node *node = pdev->dev.of_node;
562f76ee892STomi Valkeinen struct device_node *ep;
563f76ee892STomi Valkeinen int r;
564f76ee892STomi Valkeinen
565ada5caa4SKuninori Morimoto ep = of_graph_get_endpoint_by_regs(node, 0, -1);
566f76ee892STomi Valkeinen if (!ep)
567f76ee892STomi Valkeinen return 0;
568f76ee892STomi Valkeinen
569f76ee892STomi Valkeinen r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
570f76ee892STomi Valkeinen if (r)
571f76ee892STomi Valkeinen goto err;
572f76ee892STomi Valkeinen
573f76ee892STomi Valkeinen of_node_put(ep);
574f76ee892STomi Valkeinen return 0;
575f76ee892STomi Valkeinen
576f76ee892STomi Valkeinen err:
577f76ee892STomi Valkeinen of_node_put(ep);
578f76ee892STomi Valkeinen return r;
579f76ee892STomi Valkeinen }
580f76ee892STomi Valkeinen
581f76ee892STomi Valkeinen /* Audio callbacks */
hdmi_audio_startup(struct device * dev,void (* abort_cb)(struct device * dev))582f76ee892STomi Valkeinen static int hdmi_audio_startup(struct device *dev,
583f76ee892STomi Valkeinen void (*abort_cb)(struct device *dev))
584f76ee892STomi Valkeinen {
585f76ee892STomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
586f76ee892STomi Valkeinen int ret = 0;
587f76ee892STomi Valkeinen
588f76ee892STomi Valkeinen mutex_lock(&hd->lock);
589f76ee892STomi Valkeinen
590f76ee892STomi Valkeinen if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
591f76ee892STomi Valkeinen ret = -EPERM;
592f76ee892STomi Valkeinen goto out;
593f76ee892STomi Valkeinen }
594f76ee892STomi Valkeinen
595f76ee892STomi Valkeinen hd->audio_abort_cb = abort_cb;
596f76ee892STomi Valkeinen
597f76ee892STomi Valkeinen out:
598f76ee892STomi Valkeinen mutex_unlock(&hd->lock);
599f76ee892STomi Valkeinen
600f76ee892STomi Valkeinen return ret;
601f76ee892STomi Valkeinen }
602f76ee892STomi Valkeinen
hdmi_audio_shutdown(struct device * dev)603f76ee892STomi Valkeinen static int hdmi_audio_shutdown(struct device *dev)
604f76ee892STomi Valkeinen {
605f76ee892STomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
606f76ee892STomi Valkeinen
607f76ee892STomi Valkeinen mutex_lock(&hd->lock);
608f76ee892STomi Valkeinen hd->audio_abort_cb = NULL;
609f76ee892STomi Valkeinen hd->audio_configured = false;
610f76ee892STomi Valkeinen hd->audio_playing = false;
611f76ee892STomi Valkeinen mutex_unlock(&hd->lock);
612f76ee892STomi Valkeinen
613f76ee892STomi Valkeinen return 0;
614f76ee892STomi Valkeinen }
615f76ee892STomi Valkeinen
hdmi_audio_start(struct device * dev)616f76ee892STomi Valkeinen static int hdmi_audio_start(struct device *dev)
617f76ee892STomi Valkeinen {
618f76ee892STomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
619f76ee892STomi Valkeinen unsigned long flags;
620f76ee892STomi Valkeinen
621f76ee892STomi Valkeinen WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
622f76ee892STomi Valkeinen
623f76ee892STomi Valkeinen spin_lock_irqsave(&hd->audio_playing_lock, flags);
624f76ee892STomi Valkeinen
625f76ee892STomi Valkeinen if (hd->display_enabled)
626f76ee892STomi Valkeinen hdmi_start_audio_stream(hd);
627f76ee892STomi Valkeinen hd->audio_playing = true;
628f76ee892STomi Valkeinen
629f76ee892STomi Valkeinen spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
630f76ee892STomi Valkeinen return 0;
631f76ee892STomi Valkeinen }
632f76ee892STomi Valkeinen
hdmi_audio_stop(struct device * dev)633f76ee892STomi Valkeinen static void hdmi_audio_stop(struct device *dev)
634f76ee892STomi Valkeinen {
635f76ee892STomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
636f76ee892STomi Valkeinen unsigned long flags;
637f76ee892STomi Valkeinen
638f76ee892STomi Valkeinen WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
639f76ee892STomi Valkeinen
640f76ee892STomi Valkeinen spin_lock_irqsave(&hd->audio_playing_lock, flags);
641f76ee892STomi Valkeinen
642f76ee892STomi Valkeinen if (hd->display_enabled)
643f76ee892STomi Valkeinen hdmi_stop_audio_stream(hd);
644f76ee892STomi Valkeinen hd->audio_playing = false;
645f76ee892STomi Valkeinen
646f76ee892STomi Valkeinen spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
647f76ee892STomi Valkeinen }
648f76ee892STomi Valkeinen
hdmi_audio_config(struct device * dev,struct omap_dss_audio * dss_audio)649f76ee892STomi Valkeinen static int hdmi_audio_config(struct device *dev,
650f76ee892STomi Valkeinen struct omap_dss_audio *dss_audio)
651f76ee892STomi Valkeinen {
652f76ee892STomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
653f76ee892STomi Valkeinen int ret;
654f76ee892STomi Valkeinen
655f76ee892STomi Valkeinen mutex_lock(&hd->lock);
656f76ee892STomi Valkeinen
657f76ee892STomi Valkeinen if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
658f76ee892STomi Valkeinen ret = -EPERM;
659f76ee892STomi Valkeinen goto out;
660f76ee892STomi Valkeinen }
661f76ee892STomi Valkeinen
662f76ee892STomi Valkeinen ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio,
663f76ee892STomi Valkeinen hd->cfg.timings.pixelclock);
664f76ee892STomi Valkeinen
665f76ee892STomi Valkeinen if (!ret) {
666f76ee892STomi Valkeinen hd->audio_configured = true;
667f76ee892STomi Valkeinen hd->audio_config = *dss_audio;
668f76ee892STomi Valkeinen }
669f76ee892STomi Valkeinen out:
670f76ee892STomi Valkeinen mutex_unlock(&hd->lock);
671f76ee892STomi Valkeinen
672f76ee892STomi Valkeinen return ret;
673f76ee892STomi Valkeinen }
674f76ee892STomi Valkeinen
675f76ee892STomi Valkeinen static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
676f76ee892STomi Valkeinen .audio_startup = hdmi_audio_startup,
677f76ee892STomi Valkeinen .audio_shutdown = hdmi_audio_shutdown,
678f76ee892STomi Valkeinen .audio_start = hdmi_audio_start,
679f76ee892STomi Valkeinen .audio_stop = hdmi_audio_stop,
680f76ee892STomi Valkeinen .audio_config = hdmi_audio_config,
681f76ee892STomi Valkeinen };
682f76ee892STomi Valkeinen
hdmi_audio_register(struct device * dev)683f76ee892STomi Valkeinen static int hdmi_audio_register(struct device *dev)
684f76ee892STomi Valkeinen {
685f76ee892STomi Valkeinen struct omap_hdmi_audio_pdata pdata = {
686f76ee892STomi Valkeinen .dev = dev,
687d20fa5a0SLaurent Pinchart .version = 5,
688f76ee892STomi Valkeinen .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp),
689f76ee892STomi Valkeinen .ops = &hdmi_audio_ops,
690f76ee892STomi Valkeinen };
691f76ee892STomi Valkeinen
692f76ee892STomi Valkeinen hdmi.audio_pdev = platform_device_register_data(
693f76ee892STomi Valkeinen dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
694f76ee892STomi Valkeinen &pdata, sizeof(pdata));
695f76ee892STomi Valkeinen
696f76ee892STomi Valkeinen if (IS_ERR(hdmi.audio_pdev))
697f76ee892STomi Valkeinen return PTR_ERR(hdmi.audio_pdev);
698f76ee892STomi Valkeinen
699f76ee892STomi Valkeinen hdmi_runtime_get();
700f76ee892STomi Valkeinen hdmi.wp_idlemode =
701f76ee892STomi Valkeinen REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
702f76ee892STomi Valkeinen hdmi_runtime_put();
703f76ee892STomi Valkeinen
704f76ee892STomi Valkeinen return 0;
705f76ee892STomi Valkeinen }
706f76ee892STomi Valkeinen
707f76ee892STomi Valkeinen /* HDMI HW IP initialisation */
hdmi5_bind(struct device * dev,struct device * master,void * data)708f76ee892STomi Valkeinen static int hdmi5_bind(struct device *dev, struct device *master, void *data)
709f76ee892STomi Valkeinen {
710f76ee892STomi Valkeinen struct platform_device *pdev = to_platform_device(dev);
711f76ee892STomi Valkeinen int r;
712f76ee892STomi Valkeinen int irq;
713f76ee892STomi Valkeinen
714f76ee892STomi Valkeinen hdmi.pdev = pdev;
7150b5e0f45SJulia Lawall platform_set_drvdata(pdev, &hdmi);
716f76ee892STomi Valkeinen
717f76ee892STomi Valkeinen mutex_init(&hdmi.lock);
718f76ee892STomi Valkeinen spin_lock_init(&hdmi.audio_playing_lock);
719f76ee892STomi Valkeinen
720f76ee892STomi Valkeinen if (pdev->dev.of_node) {
721f76ee892STomi Valkeinen r = hdmi_probe_of(pdev);
722f76ee892STomi Valkeinen if (r)
723f76ee892STomi Valkeinen return r;
724f76ee892STomi Valkeinen }
725f76ee892STomi Valkeinen
726f76ee892STomi Valkeinen r = hdmi_wp_init(pdev, &hdmi.wp);
727f76ee892STomi Valkeinen if (r)
728f76ee892STomi Valkeinen return r;
729f76ee892STomi Valkeinen
730f76ee892STomi Valkeinen r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
731f76ee892STomi Valkeinen if (r)
732f76ee892STomi Valkeinen return r;
733f76ee892STomi Valkeinen
734f76ee892STomi Valkeinen r = hdmi_phy_init(pdev, &hdmi.phy);
735f76ee892STomi Valkeinen if (r)
736f76ee892STomi Valkeinen goto err;
737f76ee892STomi Valkeinen
738f76ee892STomi Valkeinen r = hdmi5_core_init(pdev, &hdmi.core);
739f76ee892STomi Valkeinen if (r)
740f76ee892STomi Valkeinen goto err;
741f76ee892STomi Valkeinen
742f76ee892STomi Valkeinen irq = platform_get_irq(pdev, 0);
743f76ee892STomi Valkeinen if (irq < 0) {
744f76ee892STomi Valkeinen DSSERR("platform_get_irq failed\n");
745f76ee892STomi Valkeinen r = -ENODEV;
746f76ee892STomi Valkeinen goto err;
747f76ee892STomi Valkeinen }
748f76ee892STomi Valkeinen
749f76ee892STomi Valkeinen r = devm_request_threaded_irq(&pdev->dev, irq,
750f76ee892STomi Valkeinen NULL, hdmi_irq_handler,
751f76ee892STomi Valkeinen IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
752f76ee892STomi Valkeinen if (r) {
753f76ee892STomi Valkeinen DSSERR("HDMI IRQ request failed\n");
754f76ee892STomi Valkeinen goto err;
755f76ee892STomi Valkeinen }
756f76ee892STomi Valkeinen
757f76ee892STomi Valkeinen pm_runtime_enable(&pdev->dev);
758f76ee892STomi Valkeinen
759f76ee892STomi Valkeinen hdmi_init_output(pdev);
760f76ee892STomi Valkeinen
761f76ee892STomi Valkeinen r = hdmi_audio_register(&pdev->dev);
762f76ee892STomi Valkeinen if (r) {
763f76ee892STomi Valkeinen DSSERR("Registering HDMI audio failed %d\n", r);
764f76ee892STomi Valkeinen hdmi_uninit_output(pdev);
765f76ee892STomi Valkeinen pm_runtime_disable(&pdev->dev);
766f76ee892STomi Valkeinen return r;
767f76ee892STomi Valkeinen }
768f76ee892STomi Valkeinen
769f76ee892STomi Valkeinen dss_debugfs_create_file("hdmi", hdmi_dump_regs);
770f76ee892STomi Valkeinen
771f76ee892STomi Valkeinen return 0;
772f76ee892STomi Valkeinen err:
773f76ee892STomi Valkeinen hdmi_pll_uninit(&hdmi.pll);
774f76ee892STomi Valkeinen return r;
775f76ee892STomi Valkeinen }
776f76ee892STomi Valkeinen
hdmi5_unbind(struct device * dev,struct device * master,void * data)777f76ee892STomi Valkeinen static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
778f76ee892STomi Valkeinen {
779f76ee892STomi Valkeinen struct platform_device *pdev = to_platform_device(dev);
780f76ee892STomi Valkeinen
781f76ee892STomi Valkeinen if (hdmi.audio_pdev)
782f76ee892STomi Valkeinen platform_device_unregister(hdmi.audio_pdev);
783f76ee892STomi Valkeinen
784f76ee892STomi Valkeinen hdmi_uninit_output(pdev);
785f76ee892STomi Valkeinen
786f76ee892STomi Valkeinen hdmi_pll_uninit(&hdmi.pll);
787f76ee892STomi Valkeinen
788f76ee892STomi Valkeinen pm_runtime_disable(&pdev->dev);
789f76ee892STomi Valkeinen }
790f76ee892STomi Valkeinen
791f76ee892STomi Valkeinen static const struct component_ops hdmi5_component_ops = {
792f76ee892STomi Valkeinen .bind = hdmi5_bind,
793f76ee892STomi Valkeinen .unbind = hdmi5_unbind,
794f76ee892STomi Valkeinen };
795f76ee892STomi Valkeinen
hdmi5_probe(struct platform_device * pdev)796f76ee892STomi Valkeinen static int hdmi5_probe(struct platform_device *pdev)
797f76ee892STomi Valkeinen {
798f76ee892STomi Valkeinen return component_add(&pdev->dev, &hdmi5_component_ops);
799f76ee892STomi Valkeinen }
800f76ee892STomi Valkeinen
hdmi5_remove(struct platform_device * pdev)801dc6b77baSUwe Kleine-König static void hdmi5_remove(struct platform_device *pdev)
802f76ee892STomi Valkeinen {
803f76ee892STomi Valkeinen component_del(&pdev->dev, &hdmi5_component_ops);
804f76ee892STomi Valkeinen }
805f76ee892STomi Valkeinen
hdmi_runtime_suspend(struct device * dev)806f76ee892STomi Valkeinen static int hdmi_runtime_suspend(struct device *dev)
807f76ee892STomi Valkeinen {
808f76ee892STomi Valkeinen dispc_runtime_put();
809f76ee892STomi Valkeinen
810f76ee892STomi Valkeinen return 0;
811f76ee892STomi Valkeinen }
812f76ee892STomi Valkeinen
hdmi_runtime_resume(struct device * dev)813f76ee892STomi Valkeinen static int hdmi_runtime_resume(struct device *dev)
814f76ee892STomi Valkeinen {
815f76ee892STomi Valkeinen int r;
816f76ee892STomi Valkeinen
817f76ee892STomi Valkeinen r = dispc_runtime_get();
818f76ee892STomi Valkeinen if (r < 0)
819f76ee892STomi Valkeinen return r;
820f76ee892STomi Valkeinen
821f76ee892STomi Valkeinen return 0;
822f76ee892STomi Valkeinen }
823f76ee892STomi Valkeinen
824f76ee892STomi Valkeinen static const struct dev_pm_ops hdmi_pm_ops = {
825f76ee892STomi Valkeinen .runtime_suspend = hdmi_runtime_suspend,
826f76ee892STomi Valkeinen .runtime_resume = hdmi_runtime_resume,
827f76ee892STomi Valkeinen };
828f76ee892STomi Valkeinen
829f76ee892STomi Valkeinen static const struct of_device_id hdmi_of_match[] = {
830f76ee892STomi Valkeinen { .compatible = "ti,omap5-hdmi", },
831f76ee892STomi Valkeinen { .compatible = "ti,dra7-hdmi", },
832f76ee892STomi Valkeinen {},
833f76ee892STomi Valkeinen };
834f76ee892STomi Valkeinen
835f76ee892STomi Valkeinen static struct platform_driver omapdss_hdmihw_driver = {
836f76ee892STomi Valkeinen .probe = hdmi5_probe,
837*01ecc142SUwe Kleine-König .remove = hdmi5_remove,
838f76ee892STomi Valkeinen .driver = {
839f76ee892STomi Valkeinen .name = "omapdss_hdmi5",
840f76ee892STomi Valkeinen .pm = &hdmi_pm_ops,
841f76ee892STomi Valkeinen .of_match_table = hdmi_of_match,
842f76ee892STomi Valkeinen .suppress_bind_attrs = true,
843f76ee892STomi Valkeinen },
844f76ee892STomi Valkeinen };
845f76ee892STomi Valkeinen
hdmi5_init_platform_driver(void)846f76ee892STomi Valkeinen int __init hdmi5_init_platform_driver(void)
847f76ee892STomi Valkeinen {
848f76ee892STomi Valkeinen return platform_driver_register(&omapdss_hdmihw_driver);
849f76ee892STomi Valkeinen }
850f76ee892STomi Valkeinen
hdmi5_uninit_platform_driver(void)851f76ee892STomi Valkeinen void hdmi5_uninit_platform_driver(void)
852f76ee892STomi Valkeinen {
853f76ee892STomi Valkeinen platform_driver_unregister(&omapdss_hdmihw_driver);
854f76ee892STomi Valkeinen }
855