xref: /linux/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c (revision f33e46a0c6bddd341d0989484a2546bba7ac4a3c)
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