xref: /openbmc/linux/drivers/gpu/drm/omapdrm/dss/hdmi4.c (revision 1b409fda60414186688d94a125ce5306f323af6d)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29960aa7cSTomi Valkeinen /*
39960aa7cSTomi Valkeinen  * HDMI interface DSS driver for TI's OMAP4 family of SoCs.
4bb5cdf8dSAndrew F. Davis  *
5*1b409fdaSAlexander A. Klimov  * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/
69960aa7cSTomi Valkeinen  * Authors: Yong Zhi
79960aa7cSTomi Valkeinen  *	Mythri pk <mythripk@ti.com>
89960aa7cSTomi Valkeinen  */
99960aa7cSTomi Valkeinen 
109960aa7cSTomi Valkeinen #define DSS_SUBSYS_NAME "HDMI"
119960aa7cSTomi Valkeinen 
129960aa7cSTomi Valkeinen #include <linux/kernel.h>
139960aa7cSTomi Valkeinen #include <linux/module.h>
149960aa7cSTomi Valkeinen #include <linux/err.h>
159960aa7cSTomi Valkeinen #include <linux/io.h>
169960aa7cSTomi Valkeinen #include <linux/interrupt.h>
179960aa7cSTomi Valkeinen #include <linux/mutex.h>
189960aa7cSTomi Valkeinen #include <linux/delay.h>
199960aa7cSTomi Valkeinen #include <linux/string.h>
209960aa7cSTomi Valkeinen #include <linux/platform_device.h>
219960aa7cSTomi Valkeinen #include <linux/pm_runtime.h>
229960aa7cSTomi Valkeinen #include <linux/clk.h>
239960aa7cSTomi Valkeinen #include <linux/regulator/consumer.h>
249960aa7cSTomi Valkeinen #include <linux/component.h>
25d9e32ecdSTomi Valkeinen #include <linux/of.h>
2609bffa6eSRob Herring #include <linux/of_graph.h>
279960aa7cSTomi Valkeinen #include <sound/omap-hdmi-audio.h>
281897e1a3SHans Verkuil #include <media/cec.h>
299960aa7cSTomi Valkeinen 
303c983905SLaurent Pinchart #include <drm/drm_atomic.h>
313c983905SLaurent Pinchart #include <drm/drm_atomic_state_helper.h>
323c983905SLaurent Pinchart 
3332043da7SPeter Ujfalusi #include "omapdss.h"
349960aa7cSTomi Valkeinen #include "hdmi4_core.h"
351897e1a3SHans Verkuil #include "hdmi4_cec.h"
369960aa7cSTomi Valkeinen #include "dss.h"
379960aa7cSTomi Valkeinen #include "hdmi.h"
389960aa7cSTomi Valkeinen 
39ac767456SLaurent Pinchart static int hdmi_runtime_get(struct omap_hdmi *hdmi)
409960aa7cSTomi Valkeinen {
419960aa7cSTomi Valkeinen 	int r;
429960aa7cSTomi Valkeinen 
439960aa7cSTomi Valkeinen 	DSSDBG("hdmi_runtime_get\n");
449960aa7cSTomi Valkeinen 
45ac767456SLaurent Pinchart 	r = pm_runtime_get_sync(&hdmi->pdev->dev);
469960aa7cSTomi Valkeinen 	WARN_ON(r < 0);
479960aa7cSTomi Valkeinen 	if (r < 0)
489960aa7cSTomi Valkeinen 		return r;
499960aa7cSTomi Valkeinen 
509960aa7cSTomi Valkeinen 	return 0;
519960aa7cSTomi Valkeinen }
529960aa7cSTomi Valkeinen 
53ac767456SLaurent Pinchart static void hdmi_runtime_put(struct omap_hdmi *hdmi)
549960aa7cSTomi Valkeinen {
559960aa7cSTomi Valkeinen 	int r;
569960aa7cSTomi Valkeinen 
579960aa7cSTomi Valkeinen 	DSSDBG("hdmi_runtime_put\n");
589960aa7cSTomi Valkeinen 
59ac767456SLaurent Pinchart 	r = pm_runtime_put_sync(&hdmi->pdev->dev);
609960aa7cSTomi Valkeinen 	WARN_ON(r < 0 && r != -ENOSYS);
619960aa7cSTomi Valkeinen }
629960aa7cSTomi Valkeinen 
639960aa7cSTomi Valkeinen static irqreturn_t hdmi_irq_handler(int irq, void *data)
649960aa7cSTomi Valkeinen {
65f3096a4aSHans Verkuil 	struct omap_hdmi *hdmi = data;
66f3096a4aSHans Verkuil 	struct hdmi_wp_data *wp = &hdmi->wp;
679960aa7cSTomi Valkeinen 	u32 irqstatus;
689960aa7cSTomi Valkeinen 
699960aa7cSTomi Valkeinen 	irqstatus = hdmi_wp_get_irqstatus(wp);
709960aa7cSTomi Valkeinen 	hdmi_wp_set_irqstatus(wp, irqstatus);
719960aa7cSTomi Valkeinen 
729960aa7cSTomi Valkeinen 	if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
739960aa7cSTomi Valkeinen 			irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
749960aa7cSTomi Valkeinen 		/*
759960aa7cSTomi Valkeinen 		 * If we get both connect and disconnect interrupts at the same
769960aa7cSTomi Valkeinen 		 * time, turn off the PHY, clear interrupts, and restart, which
779960aa7cSTomi Valkeinen 		 * raises connect interrupt if a cable is connected, or nothing
789960aa7cSTomi Valkeinen 		 * if cable is not connected.
799960aa7cSTomi Valkeinen 		 */
809960aa7cSTomi Valkeinen 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
819960aa7cSTomi Valkeinen 
829960aa7cSTomi Valkeinen 		hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
839960aa7cSTomi Valkeinen 				HDMI_IRQ_LINK_DISCONNECT);
849960aa7cSTomi Valkeinen 
859960aa7cSTomi Valkeinen 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
869960aa7cSTomi Valkeinen 	} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
879960aa7cSTomi Valkeinen 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
889960aa7cSTomi Valkeinen 	} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
899960aa7cSTomi Valkeinen 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
909960aa7cSTomi Valkeinen 	}
911897e1a3SHans Verkuil 	if (irqstatus & HDMI_IRQ_CORE) {
921897e1a3SHans Verkuil 		u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4);
931897e1a3SHans Verkuil 
941897e1a3SHans Verkuil 		hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4);
951897e1a3SHans Verkuil 		if (intr4 & 8)
961897e1a3SHans Verkuil 			hdmi4_cec_irq(&hdmi->core);
971897e1a3SHans Verkuil 	}
989960aa7cSTomi Valkeinen 
999960aa7cSTomi Valkeinen 	return IRQ_HANDLED;
1009960aa7cSTomi Valkeinen }
1019960aa7cSTomi Valkeinen 
102ac767456SLaurent Pinchart static int hdmi_power_on_core(struct omap_hdmi *hdmi)
1039960aa7cSTomi Valkeinen {
1049960aa7cSTomi Valkeinen 	int r;
1059960aa7cSTomi Valkeinen 
106ac767456SLaurent Pinchart 	if (hdmi->core.core_pwr_cnt++)
107a141a296SHans Verkuil 		return 0;
108a141a296SHans Verkuil 
109ac767456SLaurent Pinchart 	r = regulator_enable(hdmi->vdda_reg);
1109960aa7cSTomi Valkeinen 	if (r)
111a141a296SHans Verkuil 		goto err_reg_enable;
1129960aa7cSTomi Valkeinen 
113ac767456SLaurent Pinchart 	r = hdmi_runtime_get(hdmi);
1149960aa7cSTomi Valkeinen 	if (r)
1159960aa7cSTomi Valkeinen 		goto err_runtime_get;
1169960aa7cSTomi Valkeinen 
117ac767456SLaurent Pinchart 	hdmi4_core_powerdown_disable(&hdmi->core);
1181d54ecf2SHans Verkuil 
1199960aa7cSTomi Valkeinen 	/* Make selection of HDMI in DSS */
120ac767456SLaurent Pinchart 	dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK);
1219960aa7cSTomi Valkeinen 
122ac767456SLaurent Pinchart 	hdmi->core_enabled = true;
1239960aa7cSTomi Valkeinen 
1249960aa7cSTomi Valkeinen 	return 0;
1259960aa7cSTomi Valkeinen 
1269960aa7cSTomi Valkeinen err_runtime_get:
127ac767456SLaurent Pinchart 	regulator_disable(hdmi->vdda_reg);
128a141a296SHans Verkuil err_reg_enable:
129ac767456SLaurent Pinchart 	hdmi->core.core_pwr_cnt--;
1309960aa7cSTomi Valkeinen 
1319960aa7cSTomi Valkeinen 	return r;
1329960aa7cSTomi Valkeinen }
1339960aa7cSTomi Valkeinen 
134ac767456SLaurent Pinchart static void hdmi_power_off_core(struct omap_hdmi *hdmi)
1359960aa7cSTomi Valkeinen {
136ac767456SLaurent Pinchart 	if (--hdmi->core.core_pwr_cnt)
137a141a296SHans Verkuil 		return;
138a141a296SHans Verkuil 
139ac767456SLaurent Pinchart 	hdmi->core_enabled = false;
1409960aa7cSTomi Valkeinen 
141ac767456SLaurent Pinchart 	hdmi_runtime_put(hdmi);
142ac767456SLaurent Pinchart 	regulator_disable(hdmi->vdda_reg);
1439960aa7cSTomi Valkeinen }
1449960aa7cSTomi Valkeinen 
145ac767456SLaurent Pinchart static int hdmi_power_on_full(struct omap_hdmi *hdmi)
1469960aa7cSTomi Valkeinen {
1479960aa7cSTomi Valkeinen 	int r;
14895e472daSLaurent Pinchart 	const struct videomode *vm;
149ac767456SLaurent Pinchart 	struct hdmi_wp_data *wp = &hdmi->wp;
1509960aa7cSTomi Valkeinen 	struct dss_pll_clock_info hdmi_cinfo = { 0 };
151d11e5c82SLaurent Pinchart 	unsigned int pc;
1529960aa7cSTomi Valkeinen 
153ac767456SLaurent Pinchart 	r = hdmi_power_on_core(hdmi);
1549960aa7cSTomi Valkeinen 	if (r)
1559960aa7cSTomi Valkeinen 		return r;
1569960aa7cSTomi Valkeinen 
1579960aa7cSTomi Valkeinen 	/* disable and clear irqs */
158f3096a4aSHans Verkuil 	hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE);
159f3096a4aSHans Verkuil 	hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE);
1609960aa7cSTomi Valkeinen 
161ac767456SLaurent Pinchart 	vm = &hdmi->cfg.vm;
1629960aa7cSTomi Valkeinen 
163da11bbbbSPeter Ujfalusi 	DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive,
164da11bbbbSPeter Ujfalusi 	       vm->vactive);
1659960aa7cSTomi Valkeinen 
166da11bbbbSPeter Ujfalusi 	pc = vm->pixelclock;
167da11bbbbSPeter Ujfalusi 	if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
16867d8ffddSTomi Valkeinen 		pc *= 2;
16967d8ffddSTomi Valkeinen 
170c107751dSTomi Valkeinen 	/* DSS_HDMI_TCLK is bitclk / 10 */
171c107751dSTomi Valkeinen 	pc *= 10;
172c107751dSTomi Valkeinen 
173ac767456SLaurent Pinchart 	dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin),
174c17dc0e3STomi Valkeinen 		pc, &hdmi_cinfo);
1759960aa7cSTomi Valkeinen 
176ac767456SLaurent Pinchart 	r = dss_pll_enable(&hdmi->pll.pll);
1779960aa7cSTomi Valkeinen 	if (r) {
1789960aa7cSTomi Valkeinen 		DSSERR("Failed to enable PLL\n");
1799960aa7cSTomi Valkeinen 		goto err_pll_enable;
1809960aa7cSTomi Valkeinen 	}
1819960aa7cSTomi Valkeinen 
182ac767456SLaurent Pinchart 	r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo);
1839960aa7cSTomi Valkeinen 	if (r) {
1849960aa7cSTomi Valkeinen 		DSSERR("Failed to configure PLL\n");
1859960aa7cSTomi Valkeinen 		goto err_pll_cfg;
1869960aa7cSTomi Valkeinen 	}
1879960aa7cSTomi Valkeinen 
188ac767456SLaurent Pinchart 	r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco,
1899960aa7cSTomi Valkeinen 		hdmi_cinfo.clkout[0]);
1909960aa7cSTomi Valkeinen 	if (r) {
1919960aa7cSTomi Valkeinen 		DSSDBG("Failed to configure PHY\n");
1929960aa7cSTomi Valkeinen 		goto err_phy_cfg;
1939960aa7cSTomi Valkeinen 	}
1949960aa7cSTomi Valkeinen 
1959960aa7cSTomi Valkeinen 	r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
1969960aa7cSTomi Valkeinen 	if (r)
1979960aa7cSTomi Valkeinen 		goto err_phy_pwr;
1989960aa7cSTomi Valkeinen 
199ac767456SLaurent Pinchart 	hdmi4_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg);
2009960aa7cSTomi Valkeinen 
201ac767456SLaurent Pinchart 	r = dss_mgr_enable(&hdmi->output);
2029960aa7cSTomi Valkeinen 	if (r)
2039960aa7cSTomi Valkeinen 		goto err_mgr_enable;
2049960aa7cSTomi Valkeinen 
205ac767456SLaurent Pinchart 	r = hdmi_wp_video_start(&hdmi->wp);
2064e4b53ceSTomi Valkeinen 	if (r)
2074e4b53ceSTomi Valkeinen 		goto err_vid_enable;
2084e4b53ceSTomi Valkeinen 
2099960aa7cSTomi Valkeinen 	hdmi_wp_set_irqenable(wp,
2109960aa7cSTomi Valkeinen 		HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
2119960aa7cSTomi Valkeinen 
2129960aa7cSTomi Valkeinen 	return 0;
2139960aa7cSTomi Valkeinen 
2149960aa7cSTomi Valkeinen err_vid_enable:
215ac767456SLaurent Pinchart 	dss_mgr_disable(&hdmi->output);
2164e4b53ceSTomi Valkeinen err_mgr_enable:
217ac767456SLaurent Pinchart 	hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
2189960aa7cSTomi Valkeinen err_phy_pwr:
2199960aa7cSTomi Valkeinen err_phy_cfg:
2209960aa7cSTomi Valkeinen err_pll_cfg:
221ac767456SLaurent Pinchart 	dss_pll_disable(&hdmi->pll.pll);
2229960aa7cSTomi Valkeinen err_pll_enable:
223ac767456SLaurent Pinchart 	hdmi_power_off_core(hdmi);
2249960aa7cSTomi Valkeinen 	return -EIO;
2259960aa7cSTomi Valkeinen }
2269960aa7cSTomi Valkeinen 
227ac767456SLaurent Pinchart static void hdmi_power_off_full(struct omap_hdmi *hdmi)
2289960aa7cSTomi Valkeinen {
229ac767456SLaurent Pinchart 	hdmi_wp_clear_irqenable(&hdmi->wp, ~HDMI_IRQ_CORE);
2309960aa7cSTomi Valkeinen 
231ac767456SLaurent Pinchart 	hdmi_wp_video_stop(&hdmi->wp);
2329960aa7cSTomi Valkeinen 
233ac767456SLaurent Pinchart 	dss_mgr_disable(&hdmi->output);
2344e4b53ceSTomi Valkeinen 
235ac767456SLaurent Pinchart 	hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
2369960aa7cSTomi Valkeinen 
237ac767456SLaurent Pinchart 	dss_pll_disable(&hdmi->pll.pll);
2389960aa7cSTomi Valkeinen 
239ac767456SLaurent Pinchart 	hdmi_power_off_core(hdmi);
2409960aa7cSTomi Valkeinen }
2419960aa7cSTomi Valkeinen 
242f33656e1SLaurent Pinchart static int hdmi_dump_regs(struct seq_file *s, void *p)
2439960aa7cSTomi Valkeinen {
244ac767456SLaurent Pinchart 	struct omap_hdmi *hdmi = s->private;
2459960aa7cSTomi Valkeinen 
246ac767456SLaurent Pinchart 	mutex_lock(&hdmi->lock);
247ac767456SLaurent Pinchart 
248ac767456SLaurent Pinchart 	if (hdmi_runtime_get(hdmi)) {
249ac767456SLaurent Pinchart 		mutex_unlock(&hdmi->lock);
250f33656e1SLaurent Pinchart 		return 0;
2519960aa7cSTomi Valkeinen 	}
2529960aa7cSTomi Valkeinen 
253ac767456SLaurent Pinchart 	hdmi_wp_dump(&hdmi->wp, s);
254ac767456SLaurent Pinchart 	hdmi_pll_dump(&hdmi->pll, s);
255ac767456SLaurent Pinchart 	hdmi_phy_dump(&hdmi->phy, s);
256ac767456SLaurent Pinchart 	hdmi4_core_dump(&hdmi->core, s);
2579960aa7cSTomi Valkeinen 
258ac767456SLaurent Pinchart 	hdmi_runtime_put(hdmi);
259ac767456SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
260f33656e1SLaurent Pinchart 	return 0;
2619960aa7cSTomi Valkeinen }
2629960aa7cSTomi Valkeinen 
2639960aa7cSTomi Valkeinen static void hdmi_start_audio_stream(struct omap_hdmi *hd)
2649960aa7cSTomi Valkeinen {
2659960aa7cSTomi Valkeinen 	hdmi_wp_audio_enable(&hd->wp, true);
2669960aa7cSTomi Valkeinen 	hdmi4_audio_start(&hd->core, &hd->wp);
2679960aa7cSTomi Valkeinen }
2689960aa7cSTomi Valkeinen 
2699960aa7cSTomi Valkeinen static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
2709960aa7cSTomi Valkeinen {
2719960aa7cSTomi Valkeinen 	hdmi4_audio_stop(&hd->core, &hd->wp);
2729960aa7cSTomi Valkeinen 	hdmi_wp_audio_enable(&hd->wp, false);
2739960aa7cSTomi Valkeinen }
2749960aa7cSTomi Valkeinen 
275ac767456SLaurent Pinchart int hdmi4_core_enable(struct hdmi_core_data *core)
2769960aa7cSTomi Valkeinen {
277ac767456SLaurent Pinchart 	struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
2789960aa7cSTomi Valkeinen 	int r = 0;
2799960aa7cSTomi Valkeinen 
2805bebbbfeSHans Verkuil 	DSSDBG("ENTER omapdss_hdmi4_core_enable\n");
2819960aa7cSTomi Valkeinen 
282ac767456SLaurent Pinchart 	mutex_lock(&hdmi->lock);
2839960aa7cSTomi Valkeinen 
284ac767456SLaurent Pinchart 	r = hdmi_power_on_core(hdmi);
2859960aa7cSTomi Valkeinen 	if (r) {
2869960aa7cSTomi Valkeinen 		DSSERR("failed to power on device\n");
2879960aa7cSTomi Valkeinen 		goto err0;
2889960aa7cSTomi Valkeinen 	}
2899960aa7cSTomi Valkeinen 
290ac767456SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
2919960aa7cSTomi Valkeinen 	return 0;
2929960aa7cSTomi Valkeinen 
2939960aa7cSTomi Valkeinen err0:
294ac767456SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
2959960aa7cSTomi Valkeinen 	return r;
2969960aa7cSTomi Valkeinen }
2979960aa7cSTomi Valkeinen 
298ac767456SLaurent Pinchart void hdmi4_core_disable(struct hdmi_core_data *core)
2999960aa7cSTomi Valkeinen {
300ac767456SLaurent Pinchart 	struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
301ac767456SLaurent Pinchart 
3025bebbbfeSHans Verkuil 	DSSDBG("Enter omapdss_hdmi4_core_disable\n");
3039960aa7cSTomi Valkeinen 
304ac767456SLaurent Pinchart 	mutex_lock(&hdmi->lock);
3059960aa7cSTomi Valkeinen 
306ac767456SLaurent Pinchart 	hdmi_power_off_core(hdmi);
3079960aa7cSTomi Valkeinen 
308ac767456SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
3099960aa7cSTomi Valkeinen }
3109960aa7cSTomi Valkeinen 
3115fc15d98SLaurent Pinchart /* -----------------------------------------------------------------------------
312ee34f23aSLaurent Pinchart  * DRM Bridge Operations
313ee34f23aSLaurent Pinchart  */
314ee34f23aSLaurent Pinchart 
315ee34f23aSLaurent Pinchart static int hdmi4_bridge_attach(struct drm_bridge *bridge,
316ee34f23aSLaurent Pinchart 			       enum drm_bridge_attach_flags flags)
317ee34f23aSLaurent Pinchart {
318ee34f23aSLaurent Pinchart 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
319ee34f23aSLaurent Pinchart 
320e7e67d9aSLaurent Pinchart 	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
321e7e67d9aSLaurent Pinchart 		return -EINVAL;
322ee34f23aSLaurent Pinchart 
323ee34f23aSLaurent Pinchart 	return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
324ee34f23aSLaurent Pinchart 				 bridge, flags);
325ee34f23aSLaurent Pinchart }
326ee34f23aSLaurent Pinchart 
3273c983905SLaurent Pinchart static void hdmi4_bridge_mode_set(struct drm_bridge *bridge,
3283c983905SLaurent Pinchart 				  const struct drm_display_mode *mode,
3293c983905SLaurent Pinchart 				  const struct drm_display_mode *adjusted_mode)
3303c983905SLaurent Pinchart {
3313c983905SLaurent Pinchart 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
3323c983905SLaurent Pinchart 
3333c983905SLaurent Pinchart 	mutex_lock(&hdmi->lock);
3343c983905SLaurent Pinchart 
3353c983905SLaurent Pinchart 	drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm);
3363c983905SLaurent Pinchart 
3373c983905SLaurent Pinchart 	dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000);
3383c983905SLaurent Pinchart 
3393c983905SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
3403c983905SLaurent Pinchart }
3413c983905SLaurent Pinchart 
3423c983905SLaurent Pinchart static void hdmi4_bridge_enable(struct drm_bridge *bridge,
3433c983905SLaurent Pinchart 				struct drm_bridge_state *bridge_state)
3443c983905SLaurent Pinchart {
3453c983905SLaurent Pinchart 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
3463c983905SLaurent Pinchart 	struct drm_atomic_state *state = bridge_state->base.state;
3473c983905SLaurent Pinchart 	struct drm_connector_state *conn_state;
3483c983905SLaurent Pinchart 	struct drm_connector *connector;
3493c983905SLaurent Pinchart 	struct drm_crtc_state *crtc_state;
3503c983905SLaurent Pinchart 	unsigned long flags;
3513c983905SLaurent Pinchart 	int ret;
3523c983905SLaurent Pinchart 
3533c983905SLaurent Pinchart 	/*
3543c983905SLaurent Pinchart 	 * None of these should fail, as the bridge can't be enabled without a
3553c983905SLaurent Pinchart 	 * valid CRTC to connector path with fully populated new states.
3563c983905SLaurent Pinchart 	 */
3573c983905SLaurent Pinchart 	connector = drm_atomic_get_new_connector_for_encoder(state,
3583c983905SLaurent Pinchart 							     bridge->encoder);
3593c983905SLaurent Pinchart 	if (WARN_ON(!connector))
3603c983905SLaurent Pinchart 		return;
3613c983905SLaurent Pinchart 	conn_state = drm_atomic_get_new_connector_state(state, connector);
3623c983905SLaurent Pinchart 	if (WARN_ON(!conn_state))
3633c983905SLaurent Pinchart 		return;
3643c983905SLaurent Pinchart 	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
3653c983905SLaurent Pinchart 	if (WARN_ON(!crtc_state))
3663c983905SLaurent Pinchart 		return;
3673c983905SLaurent Pinchart 
3683c983905SLaurent Pinchart 	hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi
3693c983905SLaurent Pinchart 				? HDMI_HDMI : HDMI_DVI;
3703c983905SLaurent Pinchart 
3713c983905SLaurent Pinchart 	if (connector->display_info.is_hdmi) {
3723c983905SLaurent Pinchart 		const struct drm_display_mode *mode;
3733c983905SLaurent Pinchart 		struct hdmi_avi_infoframe avi;
3743c983905SLaurent Pinchart 
3753c983905SLaurent Pinchart 		mode = &crtc_state->adjusted_mode;
3763c983905SLaurent Pinchart 		ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
3773c983905SLaurent Pinchart 							       mode);
3783c983905SLaurent Pinchart 		if (ret == 0)
3793c983905SLaurent Pinchart 			hdmi->cfg.infoframe = avi;
3803c983905SLaurent Pinchart 	}
3813c983905SLaurent Pinchart 
3823c983905SLaurent Pinchart 	mutex_lock(&hdmi->lock);
3833c983905SLaurent Pinchart 
3843c983905SLaurent Pinchart 	ret = hdmi_power_on_full(hdmi);
3853c983905SLaurent Pinchart 	if (ret) {
3863c983905SLaurent Pinchart 		DSSERR("failed to power on device\n");
3873c983905SLaurent Pinchart 		goto done;
3883c983905SLaurent Pinchart 	}
3893c983905SLaurent Pinchart 
3903c983905SLaurent Pinchart 	if (hdmi->audio_configured) {
3913c983905SLaurent Pinchart 		ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp,
3923c983905SLaurent Pinchart 					 &hdmi->audio_config,
3933c983905SLaurent Pinchart 					 hdmi->cfg.vm.pixelclock);
3943c983905SLaurent Pinchart 		if (ret) {
3953c983905SLaurent Pinchart 			DSSERR("Error restoring audio configuration: %d", ret);
3963c983905SLaurent Pinchart 			hdmi->audio_abort_cb(&hdmi->pdev->dev);
3973c983905SLaurent Pinchart 			hdmi->audio_configured = false;
3983c983905SLaurent Pinchart 		}
3993c983905SLaurent Pinchart 	}
4003c983905SLaurent Pinchart 
4013c983905SLaurent Pinchart 	spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
4023c983905SLaurent Pinchart 	if (hdmi->audio_configured && hdmi->audio_playing)
4033c983905SLaurent Pinchart 		hdmi_start_audio_stream(hdmi);
4043c983905SLaurent Pinchart 	hdmi->display_enabled = true;
4053c983905SLaurent Pinchart 	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
4063c983905SLaurent Pinchart 
4073c983905SLaurent Pinchart done:
4083c983905SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
4093c983905SLaurent Pinchart }
4103c983905SLaurent Pinchart 
4113c983905SLaurent Pinchart static void hdmi4_bridge_disable(struct drm_bridge *bridge,
4123c983905SLaurent Pinchart 				 struct drm_bridge_state *bridge_state)
4133c983905SLaurent Pinchart {
4143c983905SLaurent Pinchart 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
4153c983905SLaurent Pinchart 	unsigned long flags;
4163c983905SLaurent Pinchart 
4173c983905SLaurent Pinchart 	mutex_lock(&hdmi->lock);
4183c983905SLaurent Pinchart 
4193c983905SLaurent Pinchart 	spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
4203c983905SLaurent Pinchart 	hdmi_stop_audio_stream(hdmi);
4213c983905SLaurent Pinchart 	hdmi->display_enabled = false;
4223c983905SLaurent Pinchart 	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
4233c983905SLaurent Pinchart 
4243c983905SLaurent Pinchart 	hdmi_power_off_full(hdmi);
4253c983905SLaurent Pinchart 
4263c983905SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
4273c983905SLaurent Pinchart }
4283c983905SLaurent Pinchart 
4296886b346SLaurent Pinchart static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
4306886b346SLaurent Pinchart 				    enum drm_connector_status status)
4316886b346SLaurent Pinchart {
4326886b346SLaurent Pinchart 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
4336886b346SLaurent Pinchart 
4346886b346SLaurent Pinchart 	if (status == connector_status_disconnected)
4356886b346SLaurent Pinchart 		hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
4366886b346SLaurent Pinchart }
4376886b346SLaurent Pinchart 
438ee34f23aSLaurent Pinchart static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge,
439ee34f23aSLaurent Pinchart 					  struct drm_connector *connector)
440ee34f23aSLaurent Pinchart {
441ee34f23aSLaurent Pinchart 	struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
4427f113085SLaurent Pinchart 	struct edid *edid = NULL;
4437f113085SLaurent Pinchart 	unsigned int cec_addr;
4447f113085SLaurent Pinchart 	bool need_enable;
4457f113085SLaurent Pinchart 	int r;
446ee34f23aSLaurent Pinchart 
4477f113085SLaurent Pinchart 	need_enable = hdmi->core_enabled == false;
4487f113085SLaurent Pinchart 
4497f113085SLaurent Pinchart 	if (need_enable) {
4507f113085SLaurent Pinchart 		r = hdmi4_core_enable(&hdmi->core);
4517f113085SLaurent Pinchart 		if (r)
4527f113085SLaurent Pinchart 			return NULL;
4537f113085SLaurent Pinchart 	}
4547f113085SLaurent Pinchart 
4557f113085SLaurent Pinchart 	mutex_lock(&hdmi->lock);
4567f113085SLaurent Pinchart 	r = hdmi_runtime_get(hdmi);
4577f113085SLaurent Pinchart 	BUG_ON(r);
4587f113085SLaurent Pinchart 
4597f113085SLaurent Pinchart 	r = hdmi4_core_ddc_init(&hdmi->core);
4607f113085SLaurent Pinchart 	if (r)
4617f113085SLaurent Pinchart 		goto done;
4627f113085SLaurent Pinchart 
4637f113085SLaurent Pinchart 	edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core);
4647f113085SLaurent Pinchart 
4657f113085SLaurent Pinchart done:
4667f113085SLaurent Pinchart 	hdmi_runtime_put(hdmi);
4677f113085SLaurent Pinchart 	mutex_unlock(&hdmi->lock);
4687f113085SLaurent Pinchart 
4697f113085SLaurent Pinchart 	if (edid && edid->extensions) {
4707f113085SLaurent Pinchart 		unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
4717f113085SLaurent Pinchart 
4727f113085SLaurent Pinchart 		cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
4737f113085SLaurent Pinchart 	} else {
4747f113085SLaurent Pinchart 		cec_addr = CEC_PHYS_ADDR_INVALID;
4757f113085SLaurent Pinchart 	}
4767f113085SLaurent Pinchart 
4777f113085SLaurent Pinchart 	hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr);
4787f113085SLaurent Pinchart 
4797f113085SLaurent Pinchart 	if (need_enable)
4807f113085SLaurent Pinchart 		hdmi4_core_disable(&hdmi->core);
4817f113085SLaurent Pinchart 
4827f113085SLaurent Pinchart 	return edid;
483ee34f23aSLaurent Pinchart }
484ee34f23aSLaurent Pinchart 
485ee34f23aSLaurent Pinchart static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
486ee34f23aSLaurent Pinchart 	.attach = hdmi4_bridge_attach,
4873c983905SLaurent Pinchart 	.mode_set = hdmi4_bridge_mode_set,
4883c983905SLaurent Pinchart 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
4893c983905SLaurent Pinchart 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
4903c983905SLaurent Pinchart 	.atomic_reset = drm_atomic_helper_bridge_reset,
4913c983905SLaurent Pinchart 	.atomic_enable = hdmi4_bridge_enable,
4923c983905SLaurent Pinchart 	.atomic_disable = hdmi4_bridge_disable,
4936886b346SLaurent Pinchart 	.hpd_notify = hdmi4_bridge_hpd_notify,
494ee34f23aSLaurent Pinchart 	.get_edid = hdmi4_bridge_get_edid,
495ee34f23aSLaurent Pinchart };
496ee34f23aSLaurent Pinchart 
497ee34f23aSLaurent Pinchart static void hdmi4_bridge_init(struct omap_hdmi *hdmi)
498ee34f23aSLaurent Pinchart {
499ee34f23aSLaurent Pinchart 	hdmi->bridge.funcs = &hdmi4_bridge_funcs;
500ee34f23aSLaurent Pinchart 	hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
501ee34f23aSLaurent Pinchart 	hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
502ee34f23aSLaurent Pinchart 	hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
503ee34f23aSLaurent Pinchart 
504ee34f23aSLaurent Pinchart 	drm_bridge_add(&hdmi->bridge);
505ee34f23aSLaurent Pinchart }
506ee34f23aSLaurent Pinchart 
507ee34f23aSLaurent Pinchart static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi)
508ee34f23aSLaurent Pinchart {
509ee34f23aSLaurent Pinchart 	drm_bridge_remove(&hdmi->bridge);
510ee34f23aSLaurent Pinchart }
511ee34f23aSLaurent Pinchart 
512ee34f23aSLaurent Pinchart /* -----------------------------------------------------------------------------
5135fc15d98SLaurent Pinchart  * Audio Callbacks
5145fc15d98SLaurent Pinchart  */
5159960aa7cSTomi Valkeinen 
5169960aa7cSTomi Valkeinen static int hdmi_audio_startup(struct device *dev,
5179960aa7cSTomi Valkeinen 			      void (*abort_cb)(struct device *dev))
5189960aa7cSTomi Valkeinen {
5199960aa7cSTomi Valkeinen 	struct omap_hdmi *hd = dev_get_drvdata(dev);
5209960aa7cSTomi Valkeinen 
5219960aa7cSTomi Valkeinen 	mutex_lock(&hd->lock);
5229960aa7cSTomi Valkeinen 
523c1899cb3SJyri Sarha 	WARN_ON(hd->audio_abort_cb != NULL);
5249960aa7cSTomi Valkeinen 
5259960aa7cSTomi Valkeinen 	hd->audio_abort_cb = abort_cb;
5269960aa7cSTomi Valkeinen 
5279960aa7cSTomi Valkeinen 	mutex_unlock(&hd->lock);
5289960aa7cSTomi Valkeinen 
529c1899cb3SJyri Sarha 	return 0;
5309960aa7cSTomi Valkeinen }
5319960aa7cSTomi Valkeinen 
5329960aa7cSTomi Valkeinen static int hdmi_audio_shutdown(struct device *dev)
5339960aa7cSTomi Valkeinen {
5349960aa7cSTomi Valkeinen 	struct omap_hdmi *hd = dev_get_drvdata(dev);
5359960aa7cSTomi Valkeinen 
5369960aa7cSTomi Valkeinen 	mutex_lock(&hd->lock);
5379960aa7cSTomi Valkeinen 	hd->audio_abort_cb = NULL;
5389960aa7cSTomi Valkeinen 	hd->audio_configured = false;
5399960aa7cSTomi Valkeinen 	hd->audio_playing = false;
5409960aa7cSTomi Valkeinen 	mutex_unlock(&hd->lock);
5419960aa7cSTomi Valkeinen 
5429960aa7cSTomi Valkeinen 	return 0;
5439960aa7cSTomi Valkeinen }
5449960aa7cSTomi Valkeinen 
5459960aa7cSTomi Valkeinen static int hdmi_audio_start(struct device *dev)
5469960aa7cSTomi Valkeinen {
5479960aa7cSTomi Valkeinen 	struct omap_hdmi *hd = dev_get_drvdata(dev);
5489960aa7cSTomi Valkeinen 	unsigned long flags;
5499960aa7cSTomi Valkeinen 
5509960aa7cSTomi Valkeinen 	spin_lock_irqsave(&hd->audio_playing_lock, flags);
5519960aa7cSTomi Valkeinen 
552c1899cb3SJyri Sarha 	if (hd->display_enabled) {
553c1899cb3SJyri Sarha 		if (!hdmi_mode_has_audio(&hd->cfg))
554c1899cb3SJyri Sarha 			DSSERR("%s: Video mode does not support audio\n",
555c1899cb3SJyri Sarha 			       __func__);
5569960aa7cSTomi Valkeinen 		hdmi_start_audio_stream(hd);
557c1899cb3SJyri Sarha 	}
5589960aa7cSTomi Valkeinen 	hd->audio_playing = true;
5599960aa7cSTomi Valkeinen 
5609960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
5619960aa7cSTomi Valkeinen 	return 0;
5629960aa7cSTomi Valkeinen }
5639960aa7cSTomi Valkeinen 
5649960aa7cSTomi Valkeinen static void hdmi_audio_stop(struct device *dev)
5659960aa7cSTomi Valkeinen {
5669960aa7cSTomi Valkeinen 	struct omap_hdmi *hd = dev_get_drvdata(dev);
5679960aa7cSTomi Valkeinen 	unsigned long flags;
5689960aa7cSTomi Valkeinen 
5699960aa7cSTomi Valkeinen 	WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
5709960aa7cSTomi Valkeinen 
5719960aa7cSTomi Valkeinen 	spin_lock_irqsave(&hd->audio_playing_lock, flags);
5729960aa7cSTomi Valkeinen 
5739960aa7cSTomi Valkeinen 	if (hd->display_enabled)
5749960aa7cSTomi Valkeinen 		hdmi_stop_audio_stream(hd);
5759960aa7cSTomi Valkeinen 	hd->audio_playing = false;
5769960aa7cSTomi Valkeinen 
5779960aa7cSTomi Valkeinen 	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
5789960aa7cSTomi Valkeinen }
5799960aa7cSTomi Valkeinen 
5809960aa7cSTomi Valkeinen static int hdmi_audio_config(struct device *dev,
5819960aa7cSTomi Valkeinen 			     struct omap_dss_audio *dss_audio)
5829960aa7cSTomi Valkeinen {
5839960aa7cSTomi Valkeinen 	struct omap_hdmi *hd = dev_get_drvdata(dev);
58477eeac24STomi Valkeinen 	int ret = 0;
5859960aa7cSTomi Valkeinen 
5869960aa7cSTomi Valkeinen 	mutex_lock(&hd->lock);
5879960aa7cSTomi Valkeinen 
588c1899cb3SJyri Sarha 	if (hd->display_enabled) {
589c1899cb3SJyri Sarha 		ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
590c1899cb3SJyri Sarha 					 hd->cfg.vm.pixelclock);
591c1899cb3SJyri Sarha 		if (ret)
5929960aa7cSTomi Valkeinen 			goto out;
5939960aa7cSTomi Valkeinen 	}
5949960aa7cSTomi Valkeinen 
5959960aa7cSTomi Valkeinen 	hd->audio_configured = true;
5969960aa7cSTomi Valkeinen 	hd->audio_config = *dss_audio;
5979960aa7cSTomi Valkeinen out:
5989960aa7cSTomi Valkeinen 	mutex_unlock(&hd->lock);
5999960aa7cSTomi Valkeinen 
6009960aa7cSTomi Valkeinen 	return ret;
6019960aa7cSTomi Valkeinen }
6029960aa7cSTomi Valkeinen 
6039960aa7cSTomi Valkeinen static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
6049960aa7cSTomi Valkeinen 	.audio_startup = hdmi_audio_startup,
6059960aa7cSTomi Valkeinen 	.audio_shutdown = hdmi_audio_shutdown,
6069960aa7cSTomi Valkeinen 	.audio_start = hdmi_audio_start,
6079960aa7cSTomi Valkeinen 	.audio_stop = hdmi_audio_stop,
6089960aa7cSTomi Valkeinen 	.audio_config = hdmi_audio_config,
6099960aa7cSTomi Valkeinen };
6109960aa7cSTomi Valkeinen 
611ac767456SLaurent Pinchart static int hdmi_audio_register(struct omap_hdmi *hdmi)
6129960aa7cSTomi Valkeinen {
6139960aa7cSTomi Valkeinen 	struct omap_hdmi_audio_pdata pdata = {
614ac767456SLaurent Pinchart 		.dev = &hdmi->pdev->dev,
615d20fa5a0SLaurent Pinchart 		.version = 4,
616ac767456SLaurent Pinchart 		.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp),
6179960aa7cSTomi Valkeinen 		.ops = &hdmi_audio_ops,
6189960aa7cSTomi Valkeinen 	};
6199960aa7cSTomi Valkeinen 
620ac767456SLaurent Pinchart 	hdmi->audio_pdev = platform_device_register_data(
621ac767456SLaurent Pinchart 		&hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
6229960aa7cSTomi Valkeinen 		&pdata, sizeof(pdata));
6239960aa7cSTomi Valkeinen 
624ac767456SLaurent Pinchart 	if (IS_ERR(hdmi->audio_pdev))
625ac767456SLaurent Pinchart 		return PTR_ERR(hdmi->audio_pdev);
6269960aa7cSTomi Valkeinen 
6279960aa7cSTomi Valkeinen 	return 0;
6289960aa7cSTomi Valkeinen }
6299960aa7cSTomi Valkeinen 
6305fc15d98SLaurent Pinchart /* -----------------------------------------------------------------------------
6315fc15d98SLaurent Pinchart  * Component Bind & Unbind
6325fc15d98SLaurent Pinchart  */
6335fc15d98SLaurent Pinchart 
6349960aa7cSTomi Valkeinen static int hdmi4_bind(struct device *dev, struct device *master, void *data)
6359960aa7cSTomi Valkeinen {
6367b295257SLaurent Pinchart 	struct dss_device *dss = dss_get_device(master);
6375fc15d98SLaurent Pinchart 	struct omap_hdmi *hdmi = dev_get_drvdata(dev);
6389960aa7cSTomi Valkeinen 	int r;
6399960aa7cSTomi Valkeinen 
640ac767456SLaurent Pinchart 	hdmi->dss = dss;
6419960aa7cSTomi Valkeinen 
642f8523b64SLaurent Pinchart 	r = hdmi_runtime_get(hdmi);
6439960aa7cSTomi Valkeinen 	if (r)
6445fc15d98SLaurent Pinchart 		return r;
6459960aa7cSTomi Valkeinen 
646f8523b64SLaurent Pinchart 	r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp);
647f8523b64SLaurent Pinchart 	if (r)
648f8523b64SLaurent Pinchart 		goto err_runtime_put;
649f8523b64SLaurent Pinchart 
6505fc15d98SLaurent Pinchart 	r = hdmi4_cec_init(hdmi->pdev, &hdmi->core, &hdmi->wp);
6519960aa7cSTomi Valkeinen 	if (r)
6525fc15d98SLaurent Pinchart 		goto err_pll_uninit;
6539960aa7cSTomi Valkeinen 
654ac767456SLaurent Pinchart 	r = hdmi_audio_register(hdmi);
6559960aa7cSTomi Valkeinen 	if (r) {
6569960aa7cSTomi Valkeinen 		DSSERR("Registering HDMI audio failed\n");
6575fc15d98SLaurent Pinchart 		goto err_cec_uninit;
6589960aa7cSTomi Valkeinen 	}
6599960aa7cSTomi Valkeinen 
660ac767456SLaurent Pinchart 	hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs,
661ac767456SLaurent Pinchart 					       hdmi);
6629960aa7cSTomi Valkeinen 
663f8523b64SLaurent Pinchart 	hdmi_runtime_put(hdmi);
664f8523b64SLaurent Pinchart 
6659960aa7cSTomi Valkeinen 	return 0;
666ac767456SLaurent Pinchart 
6675fc15d98SLaurent Pinchart err_cec_uninit:
6685fc15d98SLaurent Pinchart 	hdmi4_cec_uninit(&hdmi->core);
6695fc15d98SLaurent Pinchart err_pll_uninit:
670ac767456SLaurent Pinchart 	hdmi_pll_uninit(&hdmi->pll);
671f8523b64SLaurent Pinchart err_runtime_put:
672f8523b64SLaurent Pinchart 	hdmi_runtime_put(hdmi);
6739960aa7cSTomi Valkeinen 	return r;
6749960aa7cSTomi Valkeinen }
6759960aa7cSTomi Valkeinen 
6769960aa7cSTomi Valkeinen static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
6779960aa7cSTomi Valkeinen {
678ac767456SLaurent Pinchart 	struct omap_hdmi *hdmi = dev_get_drvdata(dev);
6799960aa7cSTomi Valkeinen 
680ac767456SLaurent Pinchart 	dss_debugfs_remove_file(hdmi->debugfs);
681f33656e1SLaurent Pinchart 
682ac767456SLaurent Pinchart 	if (hdmi->audio_pdev)
683ac767456SLaurent Pinchart 		platform_device_unregister(hdmi->audio_pdev);
6849960aa7cSTomi Valkeinen 
685ac767456SLaurent Pinchart 	hdmi4_cec_uninit(&hdmi->core);
686ac767456SLaurent Pinchart 	hdmi_pll_uninit(&hdmi->pll);
6879960aa7cSTomi Valkeinen }
6889960aa7cSTomi Valkeinen 
6899960aa7cSTomi Valkeinen static const struct component_ops hdmi4_component_ops = {
6909960aa7cSTomi Valkeinen 	.bind	= hdmi4_bind,
6919960aa7cSTomi Valkeinen 	.unbind	= hdmi4_unbind,
6929960aa7cSTomi Valkeinen };
6939960aa7cSTomi Valkeinen 
6945fc15d98SLaurent Pinchart /* -----------------------------------------------------------------------------
6955fc15d98SLaurent Pinchart  * Probe & Remove, Suspend & Resume
6965fc15d98SLaurent Pinchart  */
6975fc15d98SLaurent Pinchart 
69827d62452SLaurent Pinchart static int hdmi4_init_output(struct omap_hdmi *hdmi)
6995fc15d98SLaurent Pinchart {
7005fc15d98SLaurent Pinchart 	struct omap_dss_device *out = &hdmi->output;
70171316556SLaurent Pinchart 	int r;
7025fc15d98SLaurent Pinchart 
703ee34f23aSLaurent Pinchart 	hdmi4_bridge_init(hdmi);
704ee34f23aSLaurent Pinchart 
7055fc15d98SLaurent Pinchart 	out->dev = &hdmi->pdev->dev;
7065fc15d98SLaurent Pinchart 	out->id = OMAP_DSS_OUTPUT_HDMI;
7070dbfc396SLaurent Pinchart 	out->type = OMAP_DISPLAY_TYPE_HDMI;
7085fc15d98SLaurent Pinchart 	out->name = "hdmi.0";
7095fc15d98SLaurent Pinchart 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
7105fc15d98SLaurent Pinchart 	out->owner = THIS_MODULE;
711c83fefd7SLaurent Pinchart 	out->of_port = 0;
7125fc15d98SLaurent Pinchart 
713ee34f23aSLaurent Pinchart 	r = omapdss_device_init_output(out, &hdmi->bridge);
714ee34f23aSLaurent Pinchart 	if (r < 0) {
715ee34f23aSLaurent Pinchart 		hdmi4_bridge_cleanup(hdmi);
71671316556SLaurent Pinchart 		return r;
717ee34f23aSLaurent Pinchart 	}
71871316556SLaurent Pinchart 
7195fc15d98SLaurent Pinchart 	omapdss_device_register(out);
72027d62452SLaurent Pinchart 
72127d62452SLaurent Pinchart 	return 0;
7225fc15d98SLaurent Pinchart }
7235fc15d98SLaurent Pinchart 
7245fc15d98SLaurent Pinchart static void hdmi4_uninit_output(struct omap_hdmi *hdmi)
7255fc15d98SLaurent Pinchart {
7265fc15d98SLaurent Pinchart 	struct omap_dss_device *out = &hdmi->output;
7275fc15d98SLaurent Pinchart 
7285fc15d98SLaurent Pinchart 	omapdss_device_unregister(out);
729d17eb453SLaurent Pinchart 	omapdss_device_cleanup_output(out);
730ee34f23aSLaurent Pinchart 
731ee34f23aSLaurent Pinchart 	hdmi4_bridge_cleanup(hdmi);
7325fc15d98SLaurent Pinchart }
7335fc15d98SLaurent Pinchart 
7345fc15d98SLaurent Pinchart static int hdmi4_probe_of(struct omap_hdmi *hdmi)
7355fc15d98SLaurent Pinchart {
7365fc15d98SLaurent Pinchart 	struct platform_device *pdev = hdmi->pdev;
7375fc15d98SLaurent Pinchart 	struct device_node *node = pdev->dev.of_node;
7385fc15d98SLaurent Pinchart 	struct device_node *ep;
7395fc15d98SLaurent Pinchart 	int r;
7405fc15d98SLaurent Pinchart 
7415fc15d98SLaurent Pinchart 	ep = of_graph_get_endpoint_by_regs(node, 0, 0);
7425fc15d98SLaurent Pinchart 	if (!ep)
7435fc15d98SLaurent Pinchart 		return 0;
7445fc15d98SLaurent Pinchart 
7455fc15d98SLaurent Pinchart 	r = hdmi_parse_lanes_of(pdev, ep, &hdmi->phy);
7465fc15d98SLaurent Pinchart 	of_node_put(ep);
7475fc15d98SLaurent Pinchart 	return r;
7485fc15d98SLaurent Pinchart }
7495fc15d98SLaurent Pinchart 
7509960aa7cSTomi Valkeinen static int hdmi4_probe(struct platform_device *pdev)
7519960aa7cSTomi Valkeinen {
7525fc15d98SLaurent Pinchart 	struct omap_hdmi *hdmi;
7535fc15d98SLaurent Pinchart 	int irq;
7545fc15d98SLaurent Pinchart 	int r;
7555fc15d98SLaurent Pinchart 
7565fc15d98SLaurent Pinchart 	hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
7575fc15d98SLaurent Pinchart 	if (!hdmi)
7585fc15d98SLaurent Pinchart 		return -ENOMEM;
7595fc15d98SLaurent Pinchart 
7605fc15d98SLaurent Pinchart 	hdmi->pdev = pdev;
7615fc15d98SLaurent Pinchart 
7625fc15d98SLaurent Pinchart 	dev_set_drvdata(&pdev->dev, hdmi);
7635fc15d98SLaurent Pinchart 
7645fc15d98SLaurent Pinchart 	mutex_init(&hdmi->lock);
7655fc15d98SLaurent Pinchart 	spin_lock_init(&hdmi->audio_playing_lock);
7665fc15d98SLaurent Pinchart 
7675fc15d98SLaurent Pinchart 	r = hdmi4_probe_of(hdmi);
7685fc15d98SLaurent Pinchart 	if (r)
7695fc15d98SLaurent Pinchart 		goto err_free;
7705fc15d98SLaurent Pinchart 
7715fc15d98SLaurent Pinchart 	r = hdmi_wp_init(pdev, &hdmi->wp, 4);
7725fc15d98SLaurent Pinchart 	if (r)
7735fc15d98SLaurent Pinchart 		goto err_free;
7745fc15d98SLaurent Pinchart 
7755fc15d98SLaurent Pinchart 	r = hdmi_phy_init(pdev, &hdmi->phy, 4);
7765fc15d98SLaurent Pinchart 	if (r)
7775fc15d98SLaurent Pinchart 		goto err_free;
7785fc15d98SLaurent Pinchart 
7795fc15d98SLaurent Pinchart 	r = hdmi4_core_init(pdev, &hdmi->core);
7805fc15d98SLaurent Pinchart 	if (r)
7815fc15d98SLaurent Pinchart 		goto err_free;
7825fc15d98SLaurent Pinchart 
7835fc15d98SLaurent Pinchart 	irq = platform_get_irq(pdev, 0);
7845fc15d98SLaurent Pinchart 	if (irq < 0) {
7855fc15d98SLaurent Pinchart 		DSSERR("platform_get_irq failed\n");
7865fc15d98SLaurent Pinchart 		r = -ENODEV;
7875fc15d98SLaurent Pinchart 		goto err_free;
7885fc15d98SLaurent Pinchart 	}
7895fc15d98SLaurent Pinchart 
7905fc15d98SLaurent Pinchart 	r = devm_request_threaded_irq(&pdev->dev, irq,
7915fc15d98SLaurent Pinchart 			NULL, hdmi_irq_handler,
7925fc15d98SLaurent Pinchart 			IRQF_ONESHOT, "OMAP HDMI", hdmi);
7935fc15d98SLaurent Pinchart 	if (r) {
7945fc15d98SLaurent Pinchart 		DSSERR("HDMI IRQ request failed\n");
7955fc15d98SLaurent Pinchart 		goto err_free;
7965fc15d98SLaurent Pinchart 	}
7975fc15d98SLaurent Pinchart 
7988a36357aSLaurent Pinchart 	hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda");
7998a36357aSLaurent Pinchart 	if (IS_ERR(hdmi->vdda_reg)) {
8008a36357aSLaurent Pinchart 		r = PTR_ERR(hdmi->vdda_reg);
8018a36357aSLaurent Pinchart 		if (r != -EPROBE_DEFER)
8028a36357aSLaurent Pinchart 			DSSERR("can't get VDDA regulator\n");
8038a36357aSLaurent Pinchart 		goto err_free;
8048a36357aSLaurent Pinchart 	}
8058a36357aSLaurent Pinchart 
8065fc15d98SLaurent Pinchart 	pm_runtime_enable(&pdev->dev);
8075fc15d98SLaurent Pinchart 
80827d62452SLaurent Pinchart 	r = hdmi4_init_output(hdmi);
80927d62452SLaurent Pinchart 	if (r)
81027d62452SLaurent Pinchart 		goto err_pm_disable;
8115fc15d98SLaurent Pinchart 
8125fc15d98SLaurent Pinchart 	r = component_add(&pdev->dev, &hdmi4_component_ops);
8135fc15d98SLaurent Pinchart 	if (r)
8145fc15d98SLaurent Pinchart 		goto err_uninit_output;
8155fc15d98SLaurent Pinchart 
8165fc15d98SLaurent Pinchart 	return 0;
8175fc15d98SLaurent Pinchart 
8185fc15d98SLaurent Pinchart err_uninit_output:
8195fc15d98SLaurent Pinchart 	hdmi4_uninit_output(hdmi);
82027d62452SLaurent Pinchart err_pm_disable:
8215fc15d98SLaurent Pinchart 	pm_runtime_disable(&pdev->dev);
8225fc15d98SLaurent Pinchart err_free:
8235fc15d98SLaurent Pinchart 	kfree(hdmi);
8245fc15d98SLaurent Pinchart 	return r;
8259960aa7cSTomi Valkeinen }
8269960aa7cSTomi Valkeinen 
8279960aa7cSTomi Valkeinen static int hdmi4_remove(struct platform_device *pdev)
8289960aa7cSTomi Valkeinen {
8295fc15d98SLaurent Pinchart 	struct omap_hdmi *hdmi = platform_get_drvdata(pdev);
8305fc15d98SLaurent Pinchart 
8319960aa7cSTomi Valkeinen 	component_del(&pdev->dev, &hdmi4_component_ops);
8325fc15d98SLaurent Pinchart 
8335fc15d98SLaurent Pinchart 	hdmi4_uninit_output(hdmi);
8345fc15d98SLaurent Pinchart 
8355fc15d98SLaurent Pinchart 	pm_runtime_disable(&pdev->dev);
8365fc15d98SLaurent Pinchart 
8375fc15d98SLaurent Pinchart 	kfree(hdmi);
8389960aa7cSTomi Valkeinen 	return 0;
8399960aa7cSTomi Valkeinen }
8409960aa7cSTomi Valkeinen 
8419960aa7cSTomi Valkeinen static const struct of_device_id hdmi_of_match[] = {
8429960aa7cSTomi Valkeinen 	{ .compatible = "ti,omap4-hdmi", },
8439960aa7cSTomi Valkeinen 	{},
8449960aa7cSTomi Valkeinen };
8459960aa7cSTomi Valkeinen 
846d66c36a3SAndrew F. Davis struct platform_driver omapdss_hdmi4hw_driver = {
8479960aa7cSTomi Valkeinen 	.probe		= hdmi4_probe,
8489960aa7cSTomi Valkeinen 	.remove		= hdmi4_remove,
8499960aa7cSTomi Valkeinen 	.driver         = {
8509960aa7cSTomi Valkeinen 		.name   = "omapdss_hdmi",
8519960aa7cSTomi Valkeinen 		.of_match_table = hdmi_of_match,
8529960aa7cSTomi Valkeinen 		.suppress_bind_attrs = true,
8539960aa7cSTomi Valkeinen 	},
8549960aa7cSTomi Valkeinen };
855