xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/omapdrm/dss/hdmi4.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * HDMI interface DSS driver for TI's OMAP4 family of SoCs.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
6*4882a593Smuzhiyun  * Authors: Yong Zhi
7*4882a593Smuzhiyun  *	Mythri pk <mythripk@ti.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #define DSS_SUBSYS_NAME "HDMI"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/err.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <linux/interrupt.h>
17*4882a593Smuzhiyun #include <linux/mutex.h>
18*4882a593Smuzhiyun #include <linux/delay.h>
19*4882a593Smuzhiyun #include <linux/string.h>
20*4882a593Smuzhiyun #include <linux/platform_device.h>
21*4882a593Smuzhiyun #include <linux/pm_runtime.h>
22*4882a593Smuzhiyun #include <linux/clk.h>
23*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
24*4882a593Smuzhiyun #include <linux/component.h>
25*4882a593Smuzhiyun #include <linux/of.h>
26*4882a593Smuzhiyun #include <linux/of_graph.h>
27*4882a593Smuzhiyun #include <sound/omap-hdmi-audio.h>
28*4882a593Smuzhiyun #include <media/cec.h>
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #include <drm/drm_atomic.h>
31*4882a593Smuzhiyun #include <drm/drm_atomic_state_helper.h>
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #include "omapdss.h"
34*4882a593Smuzhiyun #include "hdmi4_core.h"
35*4882a593Smuzhiyun #include "hdmi4_cec.h"
36*4882a593Smuzhiyun #include "dss.h"
37*4882a593Smuzhiyun #include "hdmi.h"
38*4882a593Smuzhiyun 
hdmi_runtime_get(struct omap_hdmi * hdmi)39*4882a593Smuzhiyun static int hdmi_runtime_get(struct omap_hdmi *hdmi)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	int r;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	DSSDBG("hdmi_runtime_get\n");
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	r = pm_runtime_get_sync(&hdmi->pdev->dev);
46*4882a593Smuzhiyun 	WARN_ON(r < 0);
47*4882a593Smuzhiyun 	if (r < 0)
48*4882a593Smuzhiyun 		return r;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	return 0;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
hdmi_runtime_put(struct omap_hdmi * hdmi)53*4882a593Smuzhiyun static void hdmi_runtime_put(struct omap_hdmi *hdmi)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun 	int r;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	DSSDBG("hdmi_runtime_put\n");
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	r = pm_runtime_put_sync(&hdmi->pdev->dev);
60*4882a593Smuzhiyun 	WARN_ON(r < 0 && r != -ENOSYS);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
hdmi_irq_handler(int irq,void * data)63*4882a593Smuzhiyun static irqreturn_t hdmi_irq_handler(int irq, void *data)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = data;
66*4882a593Smuzhiyun 	struct hdmi_wp_data *wp = &hdmi->wp;
67*4882a593Smuzhiyun 	u32 irqstatus;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	irqstatus = hdmi_wp_get_irqstatus(wp);
70*4882a593Smuzhiyun 	hdmi_wp_set_irqstatus(wp, irqstatus);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
73*4882a593Smuzhiyun 			irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
74*4882a593Smuzhiyun 		/*
75*4882a593Smuzhiyun 		 * If we get both connect and disconnect interrupts at the same
76*4882a593Smuzhiyun 		 * time, turn off the PHY, clear interrupts, and restart, which
77*4882a593Smuzhiyun 		 * raises connect interrupt if a cable is connected, or nothing
78*4882a593Smuzhiyun 		 * if cable is not connected.
79*4882a593Smuzhiyun 		 */
80*4882a593Smuzhiyun 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 		hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
83*4882a593Smuzhiyun 				HDMI_IRQ_LINK_DISCONNECT);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
86*4882a593Smuzhiyun 	} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
87*4882a593Smuzhiyun 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
88*4882a593Smuzhiyun 	} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
89*4882a593Smuzhiyun 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
90*4882a593Smuzhiyun 	}
91*4882a593Smuzhiyun 	if (irqstatus & HDMI_IRQ_CORE) {
92*4882a593Smuzhiyun 		u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 		hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4);
95*4882a593Smuzhiyun 		if (intr4 & 8)
96*4882a593Smuzhiyun 			hdmi4_cec_irq(&hdmi->core);
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	return IRQ_HANDLED;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
hdmi_power_on_core(struct omap_hdmi * hdmi)102*4882a593Smuzhiyun static int hdmi_power_on_core(struct omap_hdmi *hdmi)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	int r;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	if (hdmi->core.core_pwr_cnt++)
107*4882a593Smuzhiyun 		return 0;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	r = regulator_enable(hdmi->vdda_reg);
110*4882a593Smuzhiyun 	if (r)
111*4882a593Smuzhiyun 		goto err_reg_enable;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	r = hdmi_runtime_get(hdmi);
114*4882a593Smuzhiyun 	if (r)
115*4882a593Smuzhiyun 		goto err_runtime_get;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	hdmi4_core_powerdown_disable(&hdmi->core);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	/* Make selection of HDMI in DSS */
120*4882a593Smuzhiyun 	dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	hdmi->core_enabled = true;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	return 0;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun err_runtime_get:
127*4882a593Smuzhiyun 	regulator_disable(hdmi->vdda_reg);
128*4882a593Smuzhiyun err_reg_enable:
129*4882a593Smuzhiyun 	hdmi->core.core_pwr_cnt--;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	return r;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
hdmi_power_off_core(struct omap_hdmi * hdmi)134*4882a593Smuzhiyun static void hdmi_power_off_core(struct omap_hdmi *hdmi)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	if (--hdmi->core.core_pwr_cnt)
137*4882a593Smuzhiyun 		return;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	hdmi->core_enabled = false;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	hdmi_runtime_put(hdmi);
142*4882a593Smuzhiyun 	regulator_disable(hdmi->vdda_reg);
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
hdmi_power_on_full(struct omap_hdmi * hdmi)145*4882a593Smuzhiyun static int hdmi_power_on_full(struct omap_hdmi *hdmi)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	int r;
148*4882a593Smuzhiyun 	const struct videomode *vm;
149*4882a593Smuzhiyun 	struct hdmi_wp_data *wp = &hdmi->wp;
150*4882a593Smuzhiyun 	struct dss_pll_clock_info hdmi_cinfo = { 0 };
151*4882a593Smuzhiyun 	unsigned int pc;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	r = hdmi_power_on_core(hdmi);
154*4882a593Smuzhiyun 	if (r)
155*4882a593Smuzhiyun 		return r;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	/* disable and clear irqs */
158*4882a593Smuzhiyun 	hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE);
159*4882a593Smuzhiyun 	hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE);
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	vm = &hdmi->cfg.vm;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive,
164*4882a593Smuzhiyun 	       vm->vactive);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	pc = vm->pixelclock;
167*4882a593Smuzhiyun 	if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
168*4882a593Smuzhiyun 		pc *= 2;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/* DSS_HDMI_TCLK is bitclk / 10 */
171*4882a593Smuzhiyun 	pc *= 10;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin),
174*4882a593Smuzhiyun 		pc, &hdmi_cinfo);
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	r = dss_pll_enable(&hdmi->pll.pll);
177*4882a593Smuzhiyun 	if (r) {
178*4882a593Smuzhiyun 		DSSERR("Failed to enable PLL\n");
179*4882a593Smuzhiyun 		goto err_pll_enable;
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo);
183*4882a593Smuzhiyun 	if (r) {
184*4882a593Smuzhiyun 		DSSERR("Failed to configure PLL\n");
185*4882a593Smuzhiyun 		goto err_pll_cfg;
186*4882a593Smuzhiyun 	}
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco,
189*4882a593Smuzhiyun 		hdmi_cinfo.clkout[0]);
190*4882a593Smuzhiyun 	if (r) {
191*4882a593Smuzhiyun 		DSSDBG("Failed to configure PHY\n");
192*4882a593Smuzhiyun 		goto err_phy_cfg;
193*4882a593Smuzhiyun 	}
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
196*4882a593Smuzhiyun 	if (r)
197*4882a593Smuzhiyun 		goto err_phy_pwr;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	hdmi4_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	r = dss_mgr_enable(&hdmi->output);
202*4882a593Smuzhiyun 	if (r)
203*4882a593Smuzhiyun 		goto err_mgr_enable;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	r = hdmi_wp_video_start(&hdmi->wp);
206*4882a593Smuzhiyun 	if (r)
207*4882a593Smuzhiyun 		goto err_vid_enable;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	hdmi_wp_set_irqenable(wp,
210*4882a593Smuzhiyun 		HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return 0;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun err_vid_enable:
215*4882a593Smuzhiyun 	dss_mgr_disable(&hdmi->output);
216*4882a593Smuzhiyun err_mgr_enable:
217*4882a593Smuzhiyun 	hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
218*4882a593Smuzhiyun err_phy_pwr:
219*4882a593Smuzhiyun err_phy_cfg:
220*4882a593Smuzhiyun err_pll_cfg:
221*4882a593Smuzhiyun 	dss_pll_disable(&hdmi->pll.pll);
222*4882a593Smuzhiyun err_pll_enable:
223*4882a593Smuzhiyun 	hdmi_power_off_core(hdmi);
224*4882a593Smuzhiyun 	return -EIO;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
hdmi_power_off_full(struct omap_hdmi * hdmi)227*4882a593Smuzhiyun static void hdmi_power_off_full(struct omap_hdmi *hdmi)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	hdmi_wp_clear_irqenable(&hdmi->wp, ~HDMI_IRQ_CORE);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	hdmi_wp_video_stop(&hdmi->wp);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	dss_mgr_disable(&hdmi->output);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	dss_pll_disable(&hdmi->pll.pll);
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	hdmi_power_off_core(hdmi);
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
hdmi_dump_regs(struct seq_file * s,void * p)242*4882a593Smuzhiyun static int hdmi_dump_regs(struct seq_file *s, void *p)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = s->private;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	if (hdmi_runtime_get(hdmi)) {
249*4882a593Smuzhiyun 		mutex_unlock(&hdmi->lock);
250*4882a593Smuzhiyun 		return 0;
251*4882a593Smuzhiyun 	}
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	hdmi_wp_dump(&hdmi->wp, s);
254*4882a593Smuzhiyun 	hdmi_pll_dump(&hdmi->pll, s);
255*4882a593Smuzhiyun 	hdmi_phy_dump(&hdmi->phy, s);
256*4882a593Smuzhiyun 	hdmi4_core_dump(&hdmi->core, s);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	hdmi_runtime_put(hdmi);
259*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
260*4882a593Smuzhiyun 	return 0;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
hdmi_start_audio_stream(struct omap_hdmi * hd)263*4882a593Smuzhiyun static void hdmi_start_audio_stream(struct omap_hdmi *hd)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	hdmi_wp_audio_enable(&hd->wp, true);
266*4882a593Smuzhiyun 	hdmi4_audio_start(&hd->core, &hd->wp);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
hdmi_stop_audio_stream(struct omap_hdmi * hd)269*4882a593Smuzhiyun static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	hdmi4_audio_stop(&hd->core, &hd->wp);
272*4882a593Smuzhiyun 	hdmi_wp_audio_enable(&hd->wp, false);
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
hdmi4_core_enable(struct hdmi_core_data * core)275*4882a593Smuzhiyun int hdmi4_core_enable(struct hdmi_core_data *core)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
278*4882a593Smuzhiyun 	int r = 0;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	DSSDBG("ENTER omapdss_hdmi4_core_enable\n");
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	r = hdmi_power_on_core(hdmi);
285*4882a593Smuzhiyun 	if (r) {
286*4882a593Smuzhiyun 		DSSERR("failed to power on device\n");
287*4882a593Smuzhiyun 		goto err0;
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
291*4882a593Smuzhiyun 	return 0;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun err0:
294*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
295*4882a593Smuzhiyun 	return r;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun 
hdmi4_core_disable(struct hdmi_core_data * core)298*4882a593Smuzhiyun void hdmi4_core_disable(struct hdmi_core_data *core)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	DSSDBG("Enter omapdss_hdmi4_core_disable\n");
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	hdmi_power_off_core(hdmi);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
312*4882a593Smuzhiyun  * DRM Bridge Operations
313*4882a593Smuzhiyun  */
314*4882a593Smuzhiyun 
hdmi4_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)315*4882a593Smuzhiyun static int hdmi4_bridge_attach(struct drm_bridge *bridge,
316*4882a593Smuzhiyun 			       enum drm_bridge_attach_flags flags)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
321*4882a593Smuzhiyun 		return -EINVAL;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
324*4882a593Smuzhiyun 				 bridge, flags);
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
hdmi4_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)327*4882a593Smuzhiyun static void hdmi4_bridge_mode_set(struct drm_bridge *bridge,
328*4882a593Smuzhiyun 				  const struct drm_display_mode *mode,
329*4882a593Smuzhiyun 				  const struct drm_display_mode *adjusted_mode)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm);
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000);
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun 
hdmi4_bridge_enable(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state)342*4882a593Smuzhiyun static void hdmi4_bridge_enable(struct drm_bridge *bridge,
343*4882a593Smuzhiyun 				struct drm_bridge_state *bridge_state)
344*4882a593Smuzhiyun {
345*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
346*4882a593Smuzhiyun 	struct drm_atomic_state *state = bridge_state->base.state;
347*4882a593Smuzhiyun 	struct drm_connector_state *conn_state;
348*4882a593Smuzhiyun 	struct drm_connector *connector;
349*4882a593Smuzhiyun 	struct drm_crtc_state *crtc_state;
350*4882a593Smuzhiyun 	unsigned long flags;
351*4882a593Smuzhiyun 	int ret;
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	/*
354*4882a593Smuzhiyun 	 * None of these should fail, as the bridge can't be enabled without a
355*4882a593Smuzhiyun 	 * valid CRTC to connector path with fully populated new states.
356*4882a593Smuzhiyun 	 */
357*4882a593Smuzhiyun 	connector = drm_atomic_get_new_connector_for_encoder(state,
358*4882a593Smuzhiyun 							     bridge->encoder);
359*4882a593Smuzhiyun 	if (WARN_ON(!connector))
360*4882a593Smuzhiyun 		return;
361*4882a593Smuzhiyun 	conn_state = drm_atomic_get_new_connector_state(state, connector);
362*4882a593Smuzhiyun 	if (WARN_ON(!conn_state))
363*4882a593Smuzhiyun 		return;
364*4882a593Smuzhiyun 	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
365*4882a593Smuzhiyun 	if (WARN_ON(!crtc_state))
366*4882a593Smuzhiyun 		return;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi
369*4882a593Smuzhiyun 				? HDMI_HDMI : HDMI_DVI;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if (connector->display_info.is_hdmi) {
372*4882a593Smuzhiyun 		const struct drm_display_mode *mode;
373*4882a593Smuzhiyun 		struct hdmi_avi_infoframe avi;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 		mode = &crtc_state->adjusted_mode;
376*4882a593Smuzhiyun 		ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
377*4882a593Smuzhiyun 							       mode);
378*4882a593Smuzhiyun 		if (ret == 0)
379*4882a593Smuzhiyun 			hdmi->cfg.infoframe = avi;
380*4882a593Smuzhiyun 	}
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	ret = hdmi_power_on_full(hdmi);
385*4882a593Smuzhiyun 	if (ret) {
386*4882a593Smuzhiyun 		DSSERR("failed to power on device\n");
387*4882a593Smuzhiyun 		goto done;
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	if (hdmi->audio_configured) {
391*4882a593Smuzhiyun 		ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp,
392*4882a593Smuzhiyun 					 &hdmi->audio_config,
393*4882a593Smuzhiyun 					 hdmi->cfg.vm.pixelclock);
394*4882a593Smuzhiyun 		if (ret) {
395*4882a593Smuzhiyun 			DSSERR("Error restoring audio configuration: %d", ret);
396*4882a593Smuzhiyun 			hdmi->audio_abort_cb(&hdmi->pdev->dev);
397*4882a593Smuzhiyun 			hdmi->audio_configured = false;
398*4882a593Smuzhiyun 		}
399*4882a593Smuzhiyun 	}
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
402*4882a593Smuzhiyun 	if (hdmi->audio_configured && hdmi->audio_playing)
403*4882a593Smuzhiyun 		hdmi_start_audio_stream(hdmi);
404*4882a593Smuzhiyun 	hdmi->display_enabled = true;
405*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun done:
408*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
hdmi4_bridge_disable(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state)411*4882a593Smuzhiyun static void hdmi4_bridge_disable(struct drm_bridge *bridge,
412*4882a593Smuzhiyun 				 struct drm_bridge_state *bridge_state)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
415*4882a593Smuzhiyun 	unsigned long flags;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
420*4882a593Smuzhiyun 	hdmi_stop_audio_stream(hdmi);
421*4882a593Smuzhiyun 	hdmi->display_enabled = false;
422*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	hdmi_power_off_full(hdmi);
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun 
hdmi4_bridge_hpd_notify(struct drm_bridge * bridge,enum drm_connector_status status)429*4882a593Smuzhiyun static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
430*4882a593Smuzhiyun 				    enum drm_connector_status status)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	if (status == connector_status_disconnected)
435*4882a593Smuzhiyun 		hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun 
hdmi4_bridge_get_edid(struct drm_bridge * bridge,struct drm_connector * connector)438*4882a593Smuzhiyun static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge,
439*4882a593Smuzhiyun 					  struct drm_connector *connector)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
442*4882a593Smuzhiyun 	struct edid *edid = NULL;
443*4882a593Smuzhiyun 	unsigned int cec_addr;
444*4882a593Smuzhiyun 	bool need_enable;
445*4882a593Smuzhiyun 	int r;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	need_enable = hdmi->core_enabled == false;
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	if (need_enable) {
450*4882a593Smuzhiyun 		r = hdmi4_core_enable(&hdmi->core);
451*4882a593Smuzhiyun 		if (r)
452*4882a593Smuzhiyun 			return NULL;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	mutex_lock(&hdmi->lock);
456*4882a593Smuzhiyun 	r = hdmi_runtime_get(hdmi);
457*4882a593Smuzhiyun 	BUG_ON(r);
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	r = hdmi4_core_ddc_init(&hdmi->core);
460*4882a593Smuzhiyun 	if (r)
461*4882a593Smuzhiyun 		goto done;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core);
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun done:
466*4882a593Smuzhiyun 	hdmi_runtime_put(hdmi);
467*4882a593Smuzhiyun 	mutex_unlock(&hdmi->lock);
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	if (edid && edid->extensions) {
470*4882a593Smuzhiyun 		unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 		cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
473*4882a593Smuzhiyun 	} else {
474*4882a593Smuzhiyun 		cec_addr = CEC_PHYS_ADDR_INVALID;
475*4882a593Smuzhiyun 	}
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr);
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	if (need_enable)
480*4882a593Smuzhiyun 		hdmi4_core_disable(&hdmi->core);
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	return edid;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
486*4882a593Smuzhiyun 	.attach = hdmi4_bridge_attach,
487*4882a593Smuzhiyun 	.mode_set = hdmi4_bridge_mode_set,
488*4882a593Smuzhiyun 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
489*4882a593Smuzhiyun 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
490*4882a593Smuzhiyun 	.atomic_reset = drm_atomic_helper_bridge_reset,
491*4882a593Smuzhiyun 	.atomic_enable = hdmi4_bridge_enable,
492*4882a593Smuzhiyun 	.atomic_disable = hdmi4_bridge_disable,
493*4882a593Smuzhiyun 	.hpd_notify = hdmi4_bridge_hpd_notify,
494*4882a593Smuzhiyun 	.get_edid = hdmi4_bridge_get_edid,
495*4882a593Smuzhiyun };
496*4882a593Smuzhiyun 
hdmi4_bridge_init(struct omap_hdmi * hdmi)497*4882a593Smuzhiyun static void hdmi4_bridge_init(struct omap_hdmi *hdmi)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	hdmi->bridge.funcs = &hdmi4_bridge_funcs;
500*4882a593Smuzhiyun 	hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
501*4882a593Smuzhiyun 	hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
502*4882a593Smuzhiyun 	hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	drm_bridge_add(&hdmi->bridge);
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun 
hdmi4_bridge_cleanup(struct omap_hdmi * hdmi)507*4882a593Smuzhiyun static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun 	drm_bridge_remove(&hdmi->bridge);
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
513*4882a593Smuzhiyun  * Audio Callbacks
514*4882a593Smuzhiyun  */
515*4882a593Smuzhiyun 
hdmi_audio_startup(struct device * dev,void (* abort_cb)(struct device * dev))516*4882a593Smuzhiyun static int hdmi_audio_startup(struct device *dev,
517*4882a593Smuzhiyun 			      void (*abort_cb)(struct device *dev))
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun 	struct omap_hdmi *hd = dev_get_drvdata(dev);
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	mutex_lock(&hd->lock);
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	WARN_ON(hd->audio_abort_cb != NULL);
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	hd->audio_abort_cb = abort_cb;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	mutex_unlock(&hd->lock);
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	return 0;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
hdmi_audio_shutdown(struct device * dev)532*4882a593Smuzhiyun static int hdmi_audio_shutdown(struct device *dev)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 	struct omap_hdmi *hd = dev_get_drvdata(dev);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	mutex_lock(&hd->lock);
537*4882a593Smuzhiyun 	hd->audio_abort_cb = NULL;
538*4882a593Smuzhiyun 	hd->audio_configured = false;
539*4882a593Smuzhiyun 	hd->audio_playing = false;
540*4882a593Smuzhiyun 	mutex_unlock(&hd->lock);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	return 0;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
hdmi_audio_start(struct device * dev)545*4882a593Smuzhiyun static int hdmi_audio_start(struct device *dev)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	struct omap_hdmi *hd = dev_get_drvdata(dev);
548*4882a593Smuzhiyun 	unsigned long flags;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	spin_lock_irqsave(&hd->audio_playing_lock, flags);
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	if (hd->display_enabled) {
553*4882a593Smuzhiyun 		if (!hdmi_mode_has_audio(&hd->cfg))
554*4882a593Smuzhiyun 			DSSERR("%s: Video mode does not support audio\n",
555*4882a593Smuzhiyun 			       __func__);
556*4882a593Smuzhiyun 		hdmi_start_audio_stream(hd);
557*4882a593Smuzhiyun 	}
558*4882a593Smuzhiyun 	hd->audio_playing = true;
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
561*4882a593Smuzhiyun 	return 0;
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun 
hdmi_audio_stop(struct device * dev)564*4882a593Smuzhiyun static void hdmi_audio_stop(struct device *dev)
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun 	struct omap_hdmi *hd = dev_get_drvdata(dev);
567*4882a593Smuzhiyun 	unsigned long flags;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	spin_lock_irqsave(&hd->audio_playing_lock, flags);
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	if (hd->display_enabled)
574*4882a593Smuzhiyun 		hdmi_stop_audio_stream(hd);
575*4882a593Smuzhiyun 	hd->audio_playing = false;
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun 
hdmi_audio_config(struct device * dev,struct omap_dss_audio * dss_audio)580*4882a593Smuzhiyun static int hdmi_audio_config(struct device *dev,
581*4882a593Smuzhiyun 			     struct omap_dss_audio *dss_audio)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun 	struct omap_hdmi *hd = dev_get_drvdata(dev);
584*4882a593Smuzhiyun 	int ret = 0;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	mutex_lock(&hd->lock);
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	if (hd->display_enabled) {
589*4882a593Smuzhiyun 		ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
590*4882a593Smuzhiyun 					 hd->cfg.vm.pixelclock);
591*4882a593Smuzhiyun 		if (ret)
592*4882a593Smuzhiyun 			goto out;
593*4882a593Smuzhiyun 	}
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	hd->audio_configured = true;
596*4882a593Smuzhiyun 	hd->audio_config = *dss_audio;
597*4882a593Smuzhiyun out:
598*4882a593Smuzhiyun 	mutex_unlock(&hd->lock);
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	return ret;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
604*4882a593Smuzhiyun 	.audio_startup = hdmi_audio_startup,
605*4882a593Smuzhiyun 	.audio_shutdown = hdmi_audio_shutdown,
606*4882a593Smuzhiyun 	.audio_start = hdmi_audio_start,
607*4882a593Smuzhiyun 	.audio_stop = hdmi_audio_stop,
608*4882a593Smuzhiyun 	.audio_config = hdmi_audio_config,
609*4882a593Smuzhiyun };
610*4882a593Smuzhiyun 
hdmi_audio_register(struct omap_hdmi * hdmi)611*4882a593Smuzhiyun static int hdmi_audio_register(struct omap_hdmi *hdmi)
612*4882a593Smuzhiyun {
613*4882a593Smuzhiyun 	struct omap_hdmi_audio_pdata pdata = {
614*4882a593Smuzhiyun 		.dev = &hdmi->pdev->dev,
615*4882a593Smuzhiyun 		.version = 4,
616*4882a593Smuzhiyun 		.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp),
617*4882a593Smuzhiyun 		.ops = &hdmi_audio_ops,
618*4882a593Smuzhiyun 	};
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	hdmi->audio_pdev = platform_device_register_data(
621*4882a593Smuzhiyun 		&hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
622*4882a593Smuzhiyun 		&pdata, sizeof(pdata));
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	if (IS_ERR(hdmi->audio_pdev))
625*4882a593Smuzhiyun 		return PTR_ERR(hdmi->audio_pdev);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	return 0;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
631*4882a593Smuzhiyun  * Component Bind & Unbind
632*4882a593Smuzhiyun  */
633*4882a593Smuzhiyun 
hdmi4_bind(struct device * dev,struct device * master,void * data)634*4882a593Smuzhiyun static int hdmi4_bind(struct device *dev, struct device *master, void *data)
635*4882a593Smuzhiyun {
636*4882a593Smuzhiyun 	struct dss_device *dss = dss_get_device(master);
637*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = dev_get_drvdata(dev);
638*4882a593Smuzhiyun 	int r;
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun 	hdmi->dss = dss;
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	r = hdmi_runtime_get(hdmi);
643*4882a593Smuzhiyun 	if (r)
644*4882a593Smuzhiyun 		return r;
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp);
647*4882a593Smuzhiyun 	if (r)
648*4882a593Smuzhiyun 		goto err_runtime_put;
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	r = hdmi4_cec_init(hdmi->pdev, &hdmi->core, &hdmi->wp);
651*4882a593Smuzhiyun 	if (r)
652*4882a593Smuzhiyun 		goto err_pll_uninit;
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	r = hdmi_audio_register(hdmi);
655*4882a593Smuzhiyun 	if (r) {
656*4882a593Smuzhiyun 		DSSERR("Registering HDMI audio failed\n");
657*4882a593Smuzhiyun 		goto err_cec_uninit;
658*4882a593Smuzhiyun 	}
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs,
661*4882a593Smuzhiyun 					       hdmi);
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	hdmi_runtime_put(hdmi);
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	return 0;
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun err_cec_uninit:
668*4882a593Smuzhiyun 	hdmi4_cec_uninit(&hdmi->core);
669*4882a593Smuzhiyun err_pll_uninit:
670*4882a593Smuzhiyun 	hdmi_pll_uninit(&hdmi->pll);
671*4882a593Smuzhiyun err_runtime_put:
672*4882a593Smuzhiyun 	hdmi_runtime_put(hdmi);
673*4882a593Smuzhiyun 	return r;
674*4882a593Smuzhiyun }
675*4882a593Smuzhiyun 
hdmi4_unbind(struct device * dev,struct device * master,void * data)676*4882a593Smuzhiyun static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
677*4882a593Smuzhiyun {
678*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = dev_get_drvdata(dev);
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	dss_debugfs_remove_file(hdmi->debugfs);
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	if (hdmi->audio_pdev)
683*4882a593Smuzhiyun 		platform_device_unregister(hdmi->audio_pdev);
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	hdmi4_cec_uninit(&hdmi->core);
686*4882a593Smuzhiyun 	hdmi_pll_uninit(&hdmi->pll);
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun static const struct component_ops hdmi4_component_ops = {
690*4882a593Smuzhiyun 	.bind	= hdmi4_bind,
691*4882a593Smuzhiyun 	.unbind	= hdmi4_unbind,
692*4882a593Smuzhiyun };
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
695*4882a593Smuzhiyun  * Probe & Remove, Suspend & Resume
696*4882a593Smuzhiyun  */
697*4882a593Smuzhiyun 
hdmi4_init_output(struct omap_hdmi * hdmi)698*4882a593Smuzhiyun static int hdmi4_init_output(struct omap_hdmi *hdmi)
699*4882a593Smuzhiyun {
700*4882a593Smuzhiyun 	struct omap_dss_device *out = &hdmi->output;
701*4882a593Smuzhiyun 	int r;
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 	hdmi4_bridge_init(hdmi);
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 	out->dev = &hdmi->pdev->dev;
706*4882a593Smuzhiyun 	out->id = OMAP_DSS_OUTPUT_HDMI;
707*4882a593Smuzhiyun 	out->type = OMAP_DISPLAY_TYPE_HDMI;
708*4882a593Smuzhiyun 	out->name = "hdmi.0";
709*4882a593Smuzhiyun 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
710*4882a593Smuzhiyun 	out->owner = THIS_MODULE;
711*4882a593Smuzhiyun 	out->of_port = 0;
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun 	r = omapdss_device_init_output(out, &hdmi->bridge);
714*4882a593Smuzhiyun 	if (r < 0) {
715*4882a593Smuzhiyun 		hdmi4_bridge_cleanup(hdmi);
716*4882a593Smuzhiyun 		return r;
717*4882a593Smuzhiyun 	}
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	omapdss_device_register(out);
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	return 0;
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun 
hdmi4_uninit_output(struct omap_hdmi * hdmi)724*4882a593Smuzhiyun static void hdmi4_uninit_output(struct omap_hdmi *hdmi)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun 	struct omap_dss_device *out = &hdmi->output;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	omapdss_device_unregister(out);
729*4882a593Smuzhiyun 	omapdss_device_cleanup_output(out);
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun 	hdmi4_bridge_cleanup(hdmi);
732*4882a593Smuzhiyun }
733*4882a593Smuzhiyun 
hdmi4_probe_of(struct omap_hdmi * hdmi)734*4882a593Smuzhiyun static int hdmi4_probe_of(struct omap_hdmi *hdmi)
735*4882a593Smuzhiyun {
736*4882a593Smuzhiyun 	struct platform_device *pdev = hdmi->pdev;
737*4882a593Smuzhiyun 	struct device_node *node = pdev->dev.of_node;
738*4882a593Smuzhiyun 	struct device_node *ep;
739*4882a593Smuzhiyun 	int r;
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	ep = of_graph_get_endpoint_by_regs(node, 0, 0);
742*4882a593Smuzhiyun 	if (!ep)
743*4882a593Smuzhiyun 		return 0;
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	r = hdmi_parse_lanes_of(pdev, ep, &hdmi->phy);
746*4882a593Smuzhiyun 	of_node_put(ep);
747*4882a593Smuzhiyun 	return r;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun 
hdmi4_probe(struct platform_device * pdev)750*4882a593Smuzhiyun static int hdmi4_probe(struct platform_device *pdev)
751*4882a593Smuzhiyun {
752*4882a593Smuzhiyun 	struct omap_hdmi *hdmi;
753*4882a593Smuzhiyun 	int irq;
754*4882a593Smuzhiyun 	int r;
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 	hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
757*4882a593Smuzhiyun 	if (!hdmi)
758*4882a593Smuzhiyun 		return -ENOMEM;
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	hdmi->pdev = pdev;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	dev_set_drvdata(&pdev->dev, hdmi);
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	mutex_init(&hdmi->lock);
765*4882a593Smuzhiyun 	spin_lock_init(&hdmi->audio_playing_lock);
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	r = hdmi4_probe_of(hdmi);
768*4882a593Smuzhiyun 	if (r)
769*4882a593Smuzhiyun 		goto err_free;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	r = hdmi_wp_init(pdev, &hdmi->wp, 4);
772*4882a593Smuzhiyun 	if (r)
773*4882a593Smuzhiyun 		goto err_free;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	r = hdmi_phy_init(pdev, &hdmi->phy, 4);
776*4882a593Smuzhiyun 	if (r)
777*4882a593Smuzhiyun 		goto err_free;
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	r = hdmi4_core_init(pdev, &hdmi->core);
780*4882a593Smuzhiyun 	if (r)
781*4882a593Smuzhiyun 		goto err_free;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	irq = platform_get_irq(pdev, 0);
784*4882a593Smuzhiyun 	if (irq < 0) {
785*4882a593Smuzhiyun 		DSSERR("platform_get_irq failed\n");
786*4882a593Smuzhiyun 		r = -ENODEV;
787*4882a593Smuzhiyun 		goto err_free;
788*4882a593Smuzhiyun 	}
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	r = devm_request_threaded_irq(&pdev->dev, irq,
791*4882a593Smuzhiyun 			NULL, hdmi_irq_handler,
792*4882a593Smuzhiyun 			IRQF_ONESHOT, "OMAP HDMI", hdmi);
793*4882a593Smuzhiyun 	if (r) {
794*4882a593Smuzhiyun 		DSSERR("HDMI IRQ request failed\n");
795*4882a593Smuzhiyun 		goto err_free;
796*4882a593Smuzhiyun 	}
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 	hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda");
799*4882a593Smuzhiyun 	if (IS_ERR(hdmi->vdda_reg)) {
800*4882a593Smuzhiyun 		r = PTR_ERR(hdmi->vdda_reg);
801*4882a593Smuzhiyun 		if (r != -EPROBE_DEFER)
802*4882a593Smuzhiyun 			DSSERR("can't get VDDA regulator\n");
803*4882a593Smuzhiyun 		goto err_free;
804*4882a593Smuzhiyun 	}
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	pm_runtime_enable(&pdev->dev);
807*4882a593Smuzhiyun 
808*4882a593Smuzhiyun 	r = hdmi4_init_output(hdmi);
809*4882a593Smuzhiyun 	if (r)
810*4882a593Smuzhiyun 		goto err_pm_disable;
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 	r = component_add(&pdev->dev, &hdmi4_component_ops);
813*4882a593Smuzhiyun 	if (r)
814*4882a593Smuzhiyun 		goto err_uninit_output;
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun 	return 0;
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun err_uninit_output:
819*4882a593Smuzhiyun 	hdmi4_uninit_output(hdmi);
820*4882a593Smuzhiyun err_pm_disable:
821*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
822*4882a593Smuzhiyun err_free:
823*4882a593Smuzhiyun 	kfree(hdmi);
824*4882a593Smuzhiyun 	return r;
825*4882a593Smuzhiyun }
826*4882a593Smuzhiyun 
hdmi4_remove(struct platform_device * pdev)827*4882a593Smuzhiyun static int hdmi4_remove(struct platform_device *pdev)
828*4882a593Smuzhiyun {
829*4882a593Smuzhiyun 	struct omap_hdmi *hdmi = platform_get_drvdata(pdev);
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun 	component_del(&pdev->dev, &hdmi4_component_ops);
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	hdmi4_uninit_output(hdmi);
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	kfree(hdmi);
838*4882a593Smuzhiyun 	return 0;
839*4882a593Smuzhiyun }
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun static const struct of_device_id hdmi_of_match[] = {
842*4882a593Smuzhiyun 	{ .compatible = "ti,omap4-hdmi", },
843*4882a593Smuzhiyun 	{},
844*4882a593Smuzhiyun };
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun struct platform_driver omapdss_hdmi4hw_driver = {
847*4882a593Smuzhiyun 	.probe		= hdmi4_probe,
848*4882a593Smuzhiyun 	.remove		= hdmi4_remove,
849*4882a593Smuzhiyun 	.driver         = {
850*4882a593Smuzhiyun 		.name   = "omapdss_hdmi",
851*4882a593Smuzhiyun 		.of_match_table = hdmi_of_match,
852*4882a593Smuzhiyun 		.suppress_bind_attrs = true,
853*4882a593Smuzhiyun 	},
854*4882a593Smuzhiyun };
855