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 *
51b409fdaSAlexander 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>
32255490f9SVille Syrjälä #include <drm/drm_edid.h>
333c983905SLaurent Pinchart
3432043da7SPeter Ujfalusi #include "omapdss.h"
359960aa7cSTomi Valkeinen #include "hdmi4_core.h"
361897e1a3SHans Verkuil #include "hdmi4_cec.h"
379960aa7cSTomi Valkeinen #include "dss.h"
389960aa7cSTomi Valkeinen #include "hdmi.h"
399960aa7cSTomi Valkeinen
hdmi_runtime_get(struct omap_hdmi * hdmi)40ac767456SLaurent Pinchart static int hdmi_runtime_get(struct omap_hdmi *hdmi)
419960aa7cSTomi Valkeinen {
429960aa7cSTomi Valkeinen int r;
439960aa7cSTomi Valkeinen
449960aa7cSTomi Valkeinen DSSDBG("hdmi_runtime_get\n");
459960aa7cSTomi Valkeinen
46ac767456SLaurent Pinchart r = pm_runtime_get_sync(&hdmi->pdev->dev);
47a5d704d3SDinghao Liu if (WARN_ON(r < 0)) {
48a5d704d3SDinghao Liu pm_runtime_put_noidle(&hdmi->pdev->dev);
499960aa7cSTomi Valkeinen return r;
50a5d704d3SDinghao Liu }
519960aa7cSTomi Valkeinen return 0;
529960aa7cSTomi Valkeinen }
539960aa7cSTomi Valkeinen
hdmi_runtime_put(struct omap_hdmi * hdmi)54ac767456SLaurent Pinchart static void hdmi_runtime_put(struct omap_hdmi *hdmi)
559960aa7cSTomi Valkeinen {
569960aa7cSTomi Valkeinen int r;
579960aa7cSTomi Valkeinen
589960aa7cSTomi Valkeinen DSSDBG("hdmi_runtime_put\n");
599960aa7cSTomi Valkeinen
60ac767456SLaurent Pinchart r = pm_runtime_put_sync(&hdmi->pdev->dev);
619960aa7cSTomi Valkeinen WARN_ON(r < 0 && r != -ENOSYS);
629960aa7cSTomi Valkeinen }
639960aa7cSTomi Valkeinen
hdmi_irq_handler(int irq,void * data)649960aa7cSTomi Valkeinen static irqreturn_t hdmi_irq_handler(int irq, void *data)
659960aa7cSTomi Valkeinen {
66f3096a4aSHans Verkuil struct omap_hdmi *hdmi = data;
67f3096a4aSHans Verkuil struct hdmi_wp_data *wp = &hdmi->wp;
689960aa7cSTomi Valkeinen u32 irqstatus;
699960aa7cSTomi Valkeinen
709960aa7cSTomi Valkeinen irqstatus = hdmi_wp_get_irqstatus(wp);
719960aa7cSTomi Valkeinen hdmi_wp_set_irqstatus(wp, irqstatus);
729960aa7cSTomi Valkeinen
739960aa7cSTomi Valkeinen if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
749960aa7cSTomi Valkeinen irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
759960aa7cSTomi Valkeinen /*
769960aa7cSTomi Valkeinen * If we get both connect and disconnect interrupts at the same
779960aa7cSTomi Valkeinen * time, turn off the PHY, clear interrupts, and restart, which
789960aa7cSTomi Valkeinen * raises connect interrupt if a cable is connected, or nothing
799960aa7cSTomi Valkeinen * if cable is not connected.
809960aa7cSTomi Valkeinen */
819960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
829960aa7cSTomi Valkeinen
839960aa7cSTomi Valkeinen hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
849960aa7cSTomi Valkeinen HDMI_IRQ_LINK_DISCONNECT);
859960aa7cSTomi Valkeinen
869960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
879960aa7cSTomi Valkeinen } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
889960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
899960aa7cSTomi Valkeinen } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
909960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
919960aa7cSTomi Valkeinen }
921897e1a3SHans Verkuil if (irqstatus & HDMI_IRQ_CORE) {
931897e1a3SHans Verkuil u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4);
941897e1a3SHans Verkuil
951897e1a3SHans Verkuil hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4);
961897e1a3SHans Verkuil if (intr4 & 8)
971897e1a3SHans Verkuil hdmi4_cec_irq(&hdmi->core);
981897e1a3SHans Verkuil }
999960aa7cSTomi Valkeinen
1009960aa7cSTomi Valkeinen return IRQ_HANDLED;
1019960aa7cSTomi Valkeinen }
1029960aa7cSTomi Valkeinen
hdmi_power_on_core(struct omap_hdmi * hdmi)103ac767456SLaurent Pinchart static int hdmi_power_on_core(struct omap_hdmi *hdmi)
1049960aa7cSTomi Valkeinen {
1059960aa7cSTomi Valkeinen int r;
1069960aa7cSTomi Valkeinen
107ac767456SLaurent Pinchart if (hdmi->core.core_pwr_cnt++)
108a141a296SHans Verkuil return 0;
109a141a296SHans Verkuil
110ac767456SLaurent Pinchart r = regulator_enable(hdmi->vdda_reg);
1119960aa7cSTomi Valkeinen if (r)
112a141a296SHans Verkuil goto err_reg_enable;
1139960aa7cSTomi Valkeinen
114ac767456SLaurent Pinchart r = hdmi_runtime_get(hdmi);
1159960aa7cSTomi Valkeinen if (r)
1169960aa7cSTomi Valkeinen goto err_runtime_get;
1179960aa7cSTomi Valkeinen
118ac767456SLaurent Pinchart hdmi4_core_powerdown_disable(&hdmi->core);
1191d54ecf2SHans Verkuil
1209960aa7cSTomi Valkeinen /* Make selection of HDMI in DSS */
121ac767456SLaurent Pinchart dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK);
1229960aa7cSTomi Valkeinen
123ac767456SLaurent Pinchart hdmi->core_enabled = true;
1249960aa7cSTomi Valkeinen
1259960aa7cSTomi Valkeinen return 0;
1269960aa7cSTomi Valkeinen
1279960aa7cSTomi Valkeinen err_runtime_get:
128ac767456SLaurent Pinchart regulator_disable(hdmi->vdda_reg);
129a141a296SHans Verkuil err_reg_enable:
130ac767456SLaurent Pinchart hdmi->core.core_pwr_cnt--;
1319960aa7cSTomi Valkeinen
1329960aa7cSTomi Valkeinen return r;
1339960aa7cSTomi Valkeinen }
1349960aa7cSTomi Valkeinen
hdmi_power_off_core(struct omap_hdmi * hdmi)135ac767456SLaurent Pinchart static void hdmi_power_off_core(struct omap_hdmi *hdmi)
1369960aa7cSTomi Valkeinen {
137ac767456SLaurent Pinchart if (--hdmi->core.core_pwr_cnt)
138a141a296SHans Verkuil return;
139a141a296SHans Verkuil
140ac767456SLaurent Pinchart hdmi->core_enabled = false;
1419960aa7cSTomi Valkeinen
142ac767456SLaurent Pinchart hdmi_runtime_put(hdmi);
143ac767456SLaurent Pinchart regulator_disable(hdmi->vdda_reg);
1449960aa7cSTomi Valkeinen }
1459960aa7cSTomi Valkeinen
hdmi_power_on_full(struct omap_hdmi * hdmi)146ac767456SLaurent Pinchart static int hdmi_power_on_full(struct omap_hdmi *hdmi)
1479960aa7cSTomi Valkeinen {
1489960aa7cSTomi Valkeinen int r;
14995e472daSLaurent Pinchart const struct videomode *vm;
150ac767456SLaurent Pinchart struct hdmi_wp_data *wp = &hdmi->wp;
1519960aa7cSTomi Valkeinen struct dss_pll_clock_info hdmi_cinfo = { 0 };
152d11e5c82SLaurent Pinchart unsigned int pc;
1539960aa7cSTomi Valkeinen
154ac767456SLaurent Pinchart r = hdmi_power_on_core(hdmi);
1559960aa7cSTomi Valkeinen if (r)
1569960aa7cSTomi Valkeinen return r;
1579960aa7cSTomi Valkeinen
1589960aa7cSTomi Valkeinen /* disable and clear irqs */
159f3096a4aSHans Verkuil hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE);
160f3096a4aSHans Verkuil hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE);
1619960aa7cSTomi Valkeinen
162ac767456SLaurent Pinchart vm = &hdmi->cfg.vm;
1639960aa7cSTomi Valkeinen
164da11bbbbSPeter Ujfalusi DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive,
165da11bbbbSPeter Ujfalusi vm->vactive);
1669960aa7cSTomi Valkeinen
167da11bbbbSPeter Ujfalusi pc = vm->pixelclock;
168da11bbbbSPeter Ujfalusi if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
16967d8ffddSTomi Valkeinen pc *= 2;
17067d8ffddSTomi Valkeinen
171c107751dSTomi Valkeinen /* DSS_HDMI_TCLK is bitclk / 10 */
172c107751dSTomi Valkeinen pc *= 10;
173c107751dSTomi Valkeinen
174ac767456SLaurent Pinchart dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin),
175c17dc0e3STomi Valkeinen pc, &hdmi_cinfo);
1769960aa7cSTomi Valkeinen
177ac767456SLaurent Pinchart r = dss_pll_enable(&hdmi->pll.pll);
1789960aa7cSTomi Valkeinen if (r) {
1799960aa7cSTomi Valkeinen DSSERR("Failed to enable PLL\n");
1809960aa7cSTomi Valkeinen goto err_pll_enable;
1819960aa7cSTomi Valkeinen }
1829960aa7cSTomi Valkeinen
183ac767456SLaurent Pinchart r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo);
1849960aa7cSTomi Valkeinen if (r) {
1859960aa7cSTomi Valkeinen DSSERR("Failed to configure PLL\n");
1869960aa7cSTomi Valkeinen goto err_pll_cfg;
1879960aa7cSTomi Valkeinen }
1889960aa7cSTomi Valkeinen
189ac767456SLaurent Pinchart r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco,
1909960aa7cSTomi Valkeinen hdmi_cinfo.clkout[0]);
1919960aa7cSTomi Valkeinen if (r) {
1929960aa7cSTomi Valkeinen DSSDBG("Failed to configure PHY\n");
1939960aa7cSTomi Valkeinen goto err_phy_cfg;
1949960aa7cSTomi Valkeinen }
1959960aa7cSTomi Valkeinen
1969960aa7cSTomi Valkeinen r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
1979960aa7cSTomi Valkeinen if (r)
1989960aa7cSTomi Valkeinen goto err_phy_pwr;
1999960aa7cSTomi Valkeinen
200ac767456SLaurent Pinchart hdmi4_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg);
2019960aa7cSTomi Valkeinen
202ac767456SLaurent Pinchart r = dss_mgr_enable(&hdmi->output);
2039960aa7cSTomi Valkeinen if (r)
2049960aa7cSTomi Valkeinen goto err_mgr_enable;
2059960aa7cSTomi Valkeinen
206ac767456SLaurent Pinchart r = hdmi_wp_video_start(&hdmi->wp);
2074e4b53ceSTomi Valkeinen if (r)
2084e4b53ceSTomi Valkeinen goto err_vid_enable;
2094e4b53ceSTomi Valkeinen
2109960aa7cSTomi Valkeinen hdmi_wp_set_irqenable(wp,
2119960aa7cSTomi Valkeinen HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
2129960aa7cSTomi Valkeinen
2139960aa7cSTomi Valkeinen return 0;
2149960aa7cSTomi Valkeinen
2159960aa7cSTomi Valkeinen err_vid_enable:
216ac767456SLaurent Pinchart dss_mgr_disable(&hdmi->output);
2174e4b53ceSTomi Valkeinen err_mgr_enable:
218ac767456SLaurent Pinchart hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
2199960aa7cSTomi Valkeinen err_phy_pwr:
2209960aa7cSTomi Valkeinen err_phy_cfg:
2219960aa7cSTomi Valkeinen err_pll_cfg:
222ac767456SLaurent Pinchart dss_pll_disable(&hdmi->pll.pll);
2239960aa7cSTomi Valkeinen err_pll_enable:
224ac767456SLaurent Pinchart hdmi_power_off_core(hdmi);
2259960aa7cSTomi Valkeinen return -EIO;
2269960aa7cSTomi Valkeinen }
2279960aa7cSTomi Valkeinen
hdmi_power_off_full(struct omap_hdmi * hdmi)228ac767456SLaurent Pinchart static void hdmi_power_off_full(struct omap_hdmi *hdmi)
2299960aa7cSTomi Valkeinen {
230ac767456SLaurent Pinchart hdmi_wp_clear_irqenable(&hdmi->wp, ~HDMI_IRQ_CORE);
2319960aa7cSTomi Valkeinen
232ac767456SLaurent Pinchart hdmi_wp_video_stop(&hdmi->wp);
2339960aa7cSTomi Valkeinen
234ac767456SLaurent Pinchart dss_mgr_disable(&hdmi->output);
2354e4b53ceSTomi Valkeinen
236ac767456SLaurent Pinchart hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
2379960aa7cSTomi Valkeinen
238ac767456SLaurent Pinchart dss_pll_disable(&hdmi->pll.pll);
2399960aa7cSTomi Valkeinen
240ac767456SLaurent Pinchart hdmi_power_off_core(hdmi);
2419960aa7cSTomi Valkeinen }
2429960aa7cSTomi Valkeinen
hdmi_dump_regs(struct seq_file * s,void * p)243f33656e1SLaurent Pinchart static int hdmi_dump_regs(struct seq_file *s, void *p)
2449960aa7cSTomi Valkeinen {
245ac767456SLaurent Pinchart struct omap_hdmi *hdmi = s->private;
2469960aa7cSTomi Valkeinen
247ac767456SLaurent Pinchart mutex_lock(&hdmi->lock);
248ac767456SLaurent Pinchart
249ac767456SLaurent Pinchart if (hdmi_runtime_get(hdmi)) {
250ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock);
251f33656e1SLaurent Pinchart return 0;
2529960aa7cSTomi Valkeinen }
2539960aa7cSTomi Valkeinen
254ac767456SLaurent Pinchart hdmi_wp_dump(&hdmi->wp, s);
255ac767456SLaurent Pinchart hdmi_pll_dump(&hdmi->pll, s);
256ac767456SLaurent Pinchart hdmi_phy_dump(&hdmi->phy, s);
257ac767456SLaurent Pinchart hdmi4_core_dump(&hdmi->core, s);
2589960aa7cSTomi Valkeinen
259ac767456SLaurent Pinchart hdmi_runtime_put(hdmi);
260ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock);
261f33656e1SLaurent Pinchart return 0;
2629960aa7cSTomi Valkeinen }
2639960aa7cSTomi Valkeinen
hdmi_start_audio_stream(struct omap_hdmi * hd)2649960aa7cSTomi Valkeinen static void hdmi_start_audio_stream(struct omap_hdmi *hd)
2659960aa7cSTomi Valkeinen {
2669960aa7cSTomi Valkeinen hdmi_wp_audio_enable(&hd->wp, true);
2679960aa7cSTomi Valkeinen hdmi4_audio_start(&hd->core, &hd->wp);
2689960aa7cSTomi Valkeinen }
2699960aa7cSTomi Valkeinen
hdmi_stop_audio_stream(struct omap_hdmi * hd)2709960aa7cSTomi Valkeinen static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
2719960aa7cSTomi Valkeinen {
2729960aa7cSTomi Valkeinen hdmi4_audio_stop(&hd->core, &hd->wp);
2739960aa7cSTomi Valkeinen hdmi_wp_audio_enable(&hd->wp, false);
2749960aa7cSTomi Valkeinen }
2759960aa7cSTomi Valkeinen
hdmi4_core_enable(struct hdmi_core_data * core)276ac767456SLaurent Pinchart int hdmi4_core_enable(struct hdmi_core_data *core)
2779960aa7cSTomi Valkeinen {
278ac767456SLaurent Pinchart struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
2799960aa7cSTomi Valkeinen int r = 0;
2809960aa7cSTomi Valkeinen
2815bebbbfeSHans Verkuil DSSDBG("ENTER omapdss_hdmi4_core_enable\n");
2829960aa7cSTomi Valkeinen
283ac767456SLaurent Pinchart mutex_lock(&hdmi->lock);
2849960aa7cSTomi Valkeinen
285ac767456SLaurent Pinchart r = hdmi_power_on_core(hdmi);
2869960aa7cSTomi Valkeinen if (r) {
2879960aa7cSTomi Valkeinen DSSERR("failed to power on device\n");
2889960aa7cSTomi Valkeinen goto err0;
2899960aa7cSTomi Valkeinen }
2909960aa7cSTomi Valkeinen
291ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock);
2929960aa7cSTomi Valkeinen return 0;
2939960aa7cSTomi Valkeinen
2949960aa7cSTomi Valkeinen err0:
295ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock);
2969960aa7cSTomi Valkeinen return r;
2979960aa7cSTomi Valkeinen }
2989960aa7cSTomi Valkeinen
hdmi4_core_disable(struct hdmi_core_data * core)299ac767456SLaurent Pinchart void hdmi4_core_disable(struct hdmi_core_data *core)
3009960aa7cSTomi Valkeinen {
301ac767456SLaurent Pinchart struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core);
302ac767456SLaurent Pinchart
3035bebbbfeSHans Verkuil DSSDBG("Enter omapdss_hdmi4_core_disable\n");
3049960aa7cSTomi Valkeinen
305ac767456SLaurent Pinchart mutex_lock(&hdmi->lock);
3069960aa7cSTomi Valkeinen
307ac767456SLaurent Pinchart hdmi_power_off_core(hdmi);
3089960aa7cSTomi Valkeinen
309ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock);
3109960aa7cSTomi Valkeinen }
3119960aa7cSTomi Valkeinen
3125fc15d98SLaurent Pinchart /* -----------------------------------------------------------------------------
313ee34f23aSLaurent Pinchart * DRM Bridge Operations
314ee34f23aSLaurent Pinchart */
315ee34f23aSLaurent Pinchart
hdmi4_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)316ee34f23aSLaurent Pinchart static int hdmi4_bridge_attach(struct drm_bridge *bridge,
317ee34f23aSLaurent Pinchart enum drm_bridge_attach_flags flags)
318ee34f23aSLaurent Pinchart {
319ee34f23aSLaurent Pinchart struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
320ee34f23aSLaurent Pinchart
321e7e67d9aSLaurent Pinchart if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
322e7e67d9aSLaurent Pinchart return -EINVAL;
323ee34f23aSLaurent Pinchart
324ee34f23aSLaurent Pinchart return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge,
325ee34f23aSLaurent Pinchart bridge, flags);
326ee34f23aSLaurent Pinchart }
327ee34f23aSLaurent Pinchart
hdmi4_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)3283c983905SLaurent Pinchart static void hdmi4_bridge_mode_set(struct drm_bridge *bridge,
3293c983905SLaurent Pinchart const struct drm_display_mode *mode,
3303c983905SLaurent Pinchart const struct drm_display_mode *adjusted_mode)
3313c983905SLaurent Pinchart {
3323c983905SLaurent Pinchart struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
3333c983905SLaurent Pinchart
3343c983905SLaurent Pinchart mutex_lock(&hdmi->lock);
3353c983905SLaurent Pinchart
3363c983905SLaurent Pinchart drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm);
3373c983905SLaurent Pinchart
3383c983905SLaurent Pinchart dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000);
3393c983905SLaurent Pinchart
3403c983905SLaurent Pinchart mutex_unlock(&hdmi->lock);
3413c983905SLaurent Pinchart }
3423c983905SLaurent Pinchart
hdmi4_bridge_enable(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state)3433c983905SLaurent Pinchart static void hdmi4_bridge_enable(struct drm_bridge *bridge,
3443c983905SLaurent Pinchart struct drm_bridge_state *bridge_state)
3453c983905SLaurent Pinchart {
3463c983905SLaurent Pinchart struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
3473c983905SLaurent Pinchart struct drm_atomic_state *state = bridge_state->base.state;
3483c983905SLaurent Pinchart struct drm_connector_state *conn_state;
3493c983905SLaurent Pinchart struct drm_connector *connector;
3503c983905SLaurent Pinchart struct drm_crtc_state *crtc_state;
3513c983905SLaurent Pinchart unsigned long flags;
3523c983905SLaurent Pinchart int ret;
3533c983905SLaurent Pinchart
3543c983905SLaurent Pinchart /*
3553c983905SLaurent Pinchart * None of these should fail, as the bridge can't be enabled without a
3563c983905SLaurent Pinchart * valid CRTC to connector path with fully populated new states.
3573c983905SLaurent Pinchart */
3583c983905SLaurent Pinchart connector = drm_atomic_get_new_connector_for_encoder(state,
3593c983905SLaurent Pinchart bridge->encoder);
3603c983905SLaurent Pinchart if (WARN_ON(!connector))
3613c983905SLaurent Pinchart return;
3623c983905SLaurent Pinchart conn_state = drm_atomic_get_new_connector_state(state, connector);
3633c983905SLaurent Pinchart if (WARN_ON(!conn_state))
3643c983905SLaurent Pinchart return;
3653c983905SLaurent Pinchart crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
3663c983905SLaurent Pinchart if (WARN_ON(!crtc_state))
3673c983905SLaurent Pinchart return;
3683c983905SLaurent Pinchart
3693c983905SLaurent Pinchart hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi
3703c983905SLaurent Pinchart ? HDMI_HDMI : HDMI_DVI;
3713c983905SLaurent Pinchart
3723c983905SLaurent Pinchart if (connector->display_info.is_hdmi) {
3733c983905SLaurent Pinchart const struct drm_display_mode *mode;
3743c983905SLaurent Pinchart struct hdmi_avi_infoframe avi;
3753c983905SLaurent Pinchart
3763c983905SLaurent Pinchart mode = &crtc_state->adjusted_mode;
3773c983905SLaurent Pinchart ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
3783c983905SLaurent Pinchart mode);
3793c983905SLaurent Pinchart if (ret == 0)
3803c983905SLaurent Pinchart hdmi->cfg.infoframe = avi;
3813c983905SLaurent Pinchart }
3823c983905SLaurent Pinchart
3833c983905SLaurent Pinchart mutex_lock(&hdmi->lock);
3843c983905SLaurent Pinchart
3853c983905SLaurent Pinchart ret = hdmi_power_on_full(hdmi);
3863c983905SLaurent Pinchart if (ret) {
3873c983905SLaurent Pinchart DSSERR("failed to power on device\n");
3883c983905SLaurent Pinchart goto done;
3893c983905SLaurent Pinchart }
3903c983905SLaurent Pinchart
3913c983905SLaurent Pinchart if (hdmi->audio_configured) {
3923c983905SLaurent Pinchart ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp,
3933c983905SLaurent Pinchart &hdmi->audio_config,
3943c983905SLaurent Pinchart hdmi->cfg.vm.pixelclock);
3953c983905SLaurent Pinchart if (ret) {
3963c983905SLaurent Pinchart DSSERR("Error restoring audio configuration: %d", ret);
3973c983905SLaurent Pinchart hdmi->audio_abort_cb(&hdmi->pdev->dev);
3983c983905SLaurent Pinchart hdmi->audio_configured = false;
3993c983905SLaurent Pinchart }
4003c983905SLaurent Pinchart }
4013c983905SLaurent Pinchart
4023c983905SLaurent Pinchart spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
4033c983905SLaurent Pinchart if (hdmi->audio_configured && hdmi->audio_playing)
4043c983905SLaurent Pinchart hdmi_start_audio_stream(hdmi);
4053c983905SLaurent Pinchart hdmi->display_enabled = true;
4063c983905SLaurent Pinchart spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
4073c983905SLaurent Pinchart
4083c983905SLaurent Pinchart done:
4093c983905SLaurent Pinchart mutex_unlock(&hdmi->lock);
4103c983905SLaurent Pinchart }
4113c983905SLaurent Pinchart
hdmi4_bridge_disable(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state)4123c983905SLaurent Pinchart static void hdmi4_bridge_disable(struct drm_bridge *bridge,
4133c983905SLaurent Pinchart struct drm_bridge_state *bridge_state)
4143c983905SLaurent Pinchart {
4153c983905SLaurent Pinchart struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
4163c983905SLaurent Pinchart unsigned long flags;
4173c983905SLaurent Pinchart
4183c983905SLaurent Pinchart mutex_lock(&hdmi->lock);
4193c983905SLaurent Pinchart
4203c983905SLaurent Pinchart spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
4213c983905SLaurent Pinchart hdmi_stop_audio_stream(hdmi);
4223c983905SLaurent Pinchart hdmi->display_enabled = false;
4233c983905SLaurent Pinchart spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
4243c983905SLaurent Pinchart
4253c983905SLaurent Pinchart hdmi_power_off_full(hdmi);
4263c983905SLaurent Pinchart
4273c983905SLaurent Pinchart mutex_unlock(&hdmi->lock);
4283c983905SLaurent Pinchart }
4293c983905SLaurent Pinchart
hdmi4_bridge_hpd_notify(struct drm_bridge * bridge,enum drm_connector_status status)4306886b346SLaurent Pinchart static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
4316886b346SLaurent Pinchart enum drm_connector_status status)
4326886b346SLaurent Pinchart {
4336886b346SLaurent Pinchart struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
4346886b346SLaurent Pinchart
4356886b346SLaurent Pinchart if (status == connector_status_disconnected)
4366886b346SLaurent Pinchart hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
4376886b346SLaurent Pinchart }
4386886b346SLaurent Pinchart
hdmi4_bridge_get_edid(struct drm_bridge * bridge,struct drm_connector * connector)439ee34f23aSLaurent Pinchart static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge,
440ee34f23aSLaurent Pinchart struct drm_connector *connector)
441ee34f23aSLaurent Pinchart {
442ee34f23aSLaurent Pinchart struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
4437f113085SLaurent Pinchart struct edid *edid = NULL;
4447f113085SLaurent Pinchart unsigned int cec_addr;
4457f113085SLaurent Pinchart bool need_enable;
4467f113085SLaurent Pinchart int r;
447ee34f23aSLaurent Pinchart
4487f113085SLaurent Pinchart need_enable = hdmi->core_enabled == false;
4497f113085SLaurent Pinchart
4507f113085SLaurent Pinchart if (need_enable) {
4517f113085SLaurent Pinchart r = hdmi4_core_enable(&hdmi->core);
4527f113085SLaurent Pinchart if (r)
4537f113085SLaurent Pinchart return NULL;
4547f113085SLaurent Pinchart }
4557f113085SLaurent Pinchart
4567f113085SLaurent Pinchart mutex_lock(&hdmi->lock);
4577f113085SLaurent Pinchart r = hdmi_runtime_get(hdmi);
4587f113085SLaurent Pinchart BUG_ON(r);
4597f113085SLaurent Pinchart
4607f113085SLaurent Pinchart r = hdmi4_core_ddc_init(&hdmi->core);
4617f113085SLaurent Pinchart if (r)
4627f113085SLaurent Pinchart goto done;
4637f113085SLaurent Pinchart
4647f113085SLaurent Pinchart edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core);
4657f113085SLaurent Pinchart
4667f113085SLaurent Pinchart done:
4677f113085SLaurent Pinchart hdmi_runtime_put(hdmi);
4687f113085SLaurent Pinchart mutex_unlock(&hdmi->lock);
4697f113085SLaurent Pinchart
4707f113085SLaurent Pinchart if (edid && edid->extensions) {
4717f113085SLaurent Pinchart unsigned int len = (edid->extensions + 1) * EDID_LENGTH;
4727f113085SLaurent Pinchart
4737f113085SLaurent Pinchart cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL);
4747f113085SLaurent Pinchart } else {
4757f113085SLaurent Pinchart cec_addr = CEC_PHYS_ADDR_INVALID;
4767f113085SLaurent Pinchart }
4777f113085SLaurent Pinchart
4787f113085SLaurent Pinchart hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr);
4797f113085SLaurent Pinchart
4807f113085SLaurent Pinchart if (need_enable)
4817f113085SLaurent Pinchart hdmi4_core_disable(&hdmi->core);
4827f113085SLaurent Pinchart
4837f113085SLaurent Pinchart return edid;
484ee34f23aSLaurent Pinchart }
485ee34f23aSLaurent Pinchart
486ee34f23aSLaurent Pinchart static const struct drm_bridge_funcs hdmi4_bridge_funcs = {
487ee34f23aSLaurent Pinchart .attach = hdmi4_bridge_attach,
4883c983905SLaurent Pinchart .mode_set = hdmi4_bridge_mode_set,
4893c983905SLaurent Pinchart .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
4903c983905SLaurent Pinchart .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
4913c983905SLaurent Pinchart .atomic_reset = drm_atomic_helper_bridge_reset,
4923c983905SLaurent Pinchart .atomic_enable = hdmi4_bridge_enable,
4933c983905SLaurent Pinchart .atomic_disable = hdmi4_bridge_disable,
4946886b346SLaurent Pinchart .hpd_notify = hdmi4_bridge_hpd_notify,
495ee34f23aSLaurent Pinchart .get_edid = hdmi4_bridge_get_edid,
496ee34f23aSLaurent Pinchart };
497ee34f23aSLaurent Pinchart
hdmi4_bridge_init(struct omap_hdmi * hdmi)498ee34f23aSLaurent Pinchart static void hdmi4_bridge_init(struct omap_hdmi *hdmi)
499ee34f23aSLaurent Pinchart {
500ee34f23aSLaurent Pinchart hdmi->bridge.funcs = &hdmi4_bridge_funcs;
501ee34f23aSLaurent Pinchart hdmi->bridge.of_node = hdmi->pdev->dev.of_node;
502ee34f23aSLaurent Pinchart hdmi->bridge.ops = DRM_BRIDGE_OP_EDID;
503ee34f23aSLaurent Pinchart hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
504ee34f23aSLaurent Pinchart
505ee34f23aSLaurent Pinchart drm_bridge_add(&hdmi->bridge);
506ee34f23aSLaurent Pinchart }
507ee34f23aSLaurent Pinchart
hdmi4_bridge_cleanup(struct omap_hdmi * hdmi)508ee34f23aSLaurent Pinchart static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi)
509ee34f23aSLaurent Pinchart {
510ee34f23aSLaurent Pinchart drm_bridge_remove(&hdmi->bridge);
511ee34f23aSLaurent Pinchart }
512ee34f23aSLaurent Pinchart
513ee34f23aSLaurent Pinchart /* -----------------------------------------------------------------------------
5145fc15d98SLaurent Pinchart * Audio Callbacks
5155fc15d98SLaurent Pinchart */
5169960aa7cSTomi Valkeinen
hdmi_audio_startup(struct device * dev,void (* abort_cb)(struct device * dev))5179960aa7cSTomi Valkeinen static int hdmi_audio_startup(struct device *dev,
5189960aa7cSTomi Valkeinen void (*abort_cb)(struct device *dev))
5199960aa7cSTomi Valkeinen {
5209960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
5219960aa7cSTomi Valkeinen
5229960aa7cSTomi Valkeinen mutex_lock(&hd->lock);
5239960aa7cSTomi Valkeinen
524c1899cb3SJyri Sarha WARN_ON(hd->audio_abort_cb != NULL);
5259960aa7cSTomi Valkeinen
5269960aa7cSTomi Valkeinen hd->audio_abort_cb = abort_cb;
5279960aa7cSTomi Valkeinen
5289960aa7cSTomi Valkeinen mutex_unlock(&hd->lock);
5299960aa7cSTomi Valkeinen
530c1899cb3SJyri Sarha return 0;
5319960aa7cSTomi Valkeinen }
5329960aa7cSTomi Valkeinen
hdmi_audio_shutdown(struct device * dev)5339960aa7cSTomi Valkeinen static int hdmi_audio_shutdown(struct device *dev)
5349960aa7cSTomi Valkeinen {
5359960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
5369960aa7cSTomi Valkeinen
5379960aa7cSTomi Valkeinen mutex_lock(&hd->lock);
5389960aa7cSTomi Valkeinen hd->audio_abort_cb = NULL;
5399960aa7cSTomi Valkeinen hd->audio_configured = false;
5409960aa7cSTomi Valkeinen hd->audio_playing = false;
5419960aa7cSTomi Valkeinen mutex_unlock(&hd->lock);
5429960aa7cSTomi Valkeinen
5439960aa7cSTomi Valkeinen return 0;
5449960aa7cSTomi Valkeinen }
5459960aa7cSTomi Valkeinen
hdmi_audio_start(struct device * dev)5469960aa7cSTomi Valkeinen static int hdmi_audio_start(struct device *dev)
5479960aa7cSTomi Valkeinen {
5489960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
5499960aa7cSTomi Valkeinen unsigned long flags;
5509960aa7cSTomi Valkeinen
5519960aa7cSTomi Valkeinen spin_lock_irqsave(&hd->audio_playing_lock, flags);
5529960aa7cSTomi Valkeinen
553c1899cb3SJyri Sarha if (hd->display_enabled) {
554c1899cb3SJyri Sarha if (!hdmi_mode_has_audio(&hd->cfg))
555c1899cb3SJyri Sarha DSSERR("%s: Video mode does not support audio\n",
556c1899cb3SJyri Sarha __func__);
5579960aa7cSTomi Valkeinen hdmi_start_audio_stream(hd);
558c1899cb3SJyri Sarha }
5599960aa7cSTomi Valkeinen hd->audio_playing = true;
5609960aa7cSTomi Valkeinen
5619960aa7cSTomi Valkeinen spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
5629960aa7cSTomi Valkeinen return 0;
5639960aa7cSTomi Valkeinen }
5649960aa7cSTomi Valkeinen
hdmi_audio_stop(struct device * dev)5659960aa7cSTomi Valkeinen static void hdmi_audio_stop(struct device *dev)
5669960aa7cSTomi Valkeinen {
5679960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
5689960aa7cSTomi Valkeinen unsigned long flags;
5699960aa7cSTomi Valkeinen
5709960aa7cSTomi Valkeinen WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
5719960aa7cSTomi Valkeinen
5729960aa7cSTomi Valkeinen spin_lock_irqsave(&hd->audio_playing_lock, flags);
5739960aa7cSTomi Valkeinen
5749960aa7cSTomi Valkeinen if (hd->display_enabled)
5759960aa7cSTomi Valkeinen hdmi_stop_audio_stream(hd);
5769960aa7cSTomi Valkeinen hd->audio_playing = false;
5779960aa7cSTomi Valkeinen
5789960aa7cSTomi Valkeinen spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
5799960aa7cSTomi Valkeinen }
5809960aa7cSTomi Valkeinen
hdmi_audio_config(struct device * dev,struct omap_dss_audio * dss_audio)5819960aa7cSTomi Valkeinen static int hdmi_audio_config(struct device *dev,
5829960aa7cSTomi Valkeinen struct omap_dss_audio *dss_audio)
5839960aa7cSTomi Valkeinen {
5849960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev);
58577eeac24STomi Valkeinen int ret = 0;
5869960aa7cSTomi Valkeinen
5879960aa7cSTomi Valkeinen mutex_lock(&hd->lock);
5889960aa7cSTomi Valkeinen
589c1899cb3SJyri Sarha if (hd->display_enabled) {
590c1899cb3SJyri Sarha ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
591c1899cb3SJyri Sarha hd->cfg.vm.pixelclock);
592c1899cb3SJyri Sarha if (ret)
5939960aa7cSTomi Valkeinen goto out;
5949960aa7cSTomi Valkeinen }
5959960aa7cSTomi Valkeinen
5969960aa7cSTomi Valkeinen hd->audio_configured = true;
5979960aa7cSTomi Valkeinen hd->audio_config = *dss_audio;
5989960aa7cSTomi Valkeinen out:
5999960aa7cSTomi Valkeinen mutex_unlock(&hd->lock);
6009960aa7cSTomi Valkeinen
6019960aa7cSTomi Valkeinen return ret;
6029960aa7cSTomi Valkeinen }
6039960aa7cSTomi Valkeinen
6049960aa7cSTomi Valkeinen static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
6059960aa7cSTomi Valkeinen .audio_startup = hdmi_audio_startup,
6069960aa7cSTomi Valkeinen .audio_shutdown = hdmi_audio_shutdown,
6079960aa7cSTomi Valkeinen .audio_start = hdmi_audio_start,
6089960aa7cSTomi Valkeinen .audio_stop = hdmi_audio_stop,
6099960aa7cSTomi Valkeinen .audio_config = hdmi_audio_config,
6109960aa7cSTomi Valkeinen };
6119960aa7cSTomi Valkeinen
hdmi_audio_register(struct omap_hdmi * hdmi)612ac767456SLaurent Pinchart static int hdmi_audio_register(struct omap_hdmi *hdmi)
6139960aa7cSTomi Valkeinen {
6149960aa7cSTomi Valkeinen struct omap_hdmi_audio_pdata pdata = {
615ac767456SLaurent Pinchart .dev = &hdmi->pdev->dev,
616d20fa5a0SLaurent Pinchart .version = 4,
617ac767456SLaurent Pinchart .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp),
6189960aa7cSTomi Valkeinen .ops = &hdmi_audio_ops,
6199960aa7cSTomi Valkeinen };
6209960aa7cSTomi Valkeinen
621ac767456SLaurent Pinchart hdmi->audio_pdev = platform_device_register_data(
622ac767456SLaurent Pinchart &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
6239960aa7cSTomi Valkeinen &pdata, sizeof(pdata));
6249960aa7cSTomi Valkeinen
625ac767456SLaurent Pinchart if (IS_ERR(hdmi->audio_pdev))
626ac767456SLaurent Pinchart return PTR_ERR(hdmi->audio_pdev);
6279960aa7cSTomi Valkeinen
6289960aa7cSTomi Valkeinen return 0;
6299960aa7cSTomi Valkeinen }
6309960aa7cSTomi Valkeinen
6315fc15d98SLaurent Pinchart /* -----------------------------------------------------------------------------
6325fc15d98SLaurent Pinchart * Component Bind & Unbind
6335fc15d98SLaurent Pinchart */
6345fc15d98SLaurent Pinchart
hdmi4_bind(struct device * dev,struct device * master,void * data)6359960aa7cSTomi Valkeinen static int hdmi4_bind(struct device *dev, struct device *master, void *data)
6369960aa7cSTomi Valkeinen {
6377b295257SLaurent Pinchart struct dss_device *dss = dss_get_device(master);
6385fc15d98SLaurent Pinchart struct omap_hdmi *hdmi = dev_get_drvdata(dev);
6399960aa7cSTomi Valkeinen int r;
6409960aa7cSTomi Valkeinen
641ac767456SLaurent Pinchart hdmi->dss = dss;
6429960aa7cSTomi Valkeinen
643f8523b64SLaurent Pinchart r = hdmi_runtime_get(hdmi);
6449960aa7cSTomi Valkeinen if (r)
6455fc15d98SLaurent Pinchart return r;
6469960aa7cSTomi Valkeinen
647f8523b64SLaurent Pinchart r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp);
648f8523b64SLaurent Pinchart if (r)
649f8523b64SLaurent Pinchart goto err_runtime_put;
650f8523b64SLaurent Pinchart
6515fc15d98SLaurent Pinchart r = hdmi4_cec_init(hdmi->pdev, &hdmi->core, &hdmi->wp);
6529960aa7cSTomi Valkeinen if (r)
6535fc15d98SLaurent Pinchart goto err_pll_uninit;
6549960aa7cSTomi Valkeinen
655ac767456SLaurent Pinchart r = hdmi_audio_register(hdmi);
6569960aa7cSTomi Valkeinen if (r) {
6579960aa7cSTomi Valkeinen DSSERR("Registering HDMI audio failed\n");
6585fc15d98SLaurent Pinchart goto err_cec_uninit;
6599960aa7cSTomi Valkeinen }
6609960aa7cSTomi Valkeinen
661ac767456SLaurent Pinchart hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs,
662ac767456SLaurent Pinchart hdmi);
6639960aa7cSTomi Valkeinen
664f8523b64SLaurent Pinchart hdmi_runtime_put(hdmi);
665f8523b64SLaurent Pinchart
6669960aa7cSTomi Valkeinen return 0;
667ac767456SLaurent Pinchart
6685fc15d98SLaurent Pinchart err_cec_uninit:
6695fc15d98SLaurent Pinchart hdmi4_cec_uninit(&hdmi->core);
6705fc15d98SLaurent Pinchart err_pll_uninit:
671ac767456SLaurent Pinchart hdmi_pll_uninit(&hdmi->pll);
672f8523b64SLaurent Pinchart err_runtime_put:
673f8523b64SLaurent Pinchart hdmi_runtime_put(hdmi);
6749960aa7cSTomi Valkeinen return r;
6759960aa7cSTomi Valkeinen }
6769960aa7cSTomi Valkeinen
hdmi4_unbind(struct device * dev,struct device * master,void * data)6779960aa7cSTomi Valkeinen static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
6789960aa7cSTomi Valkeinen {
679ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dev_get_drvdata(dev);
6809960aa7cSTomi Valkeinen
681ac767456SLaurent Pinchart dss_debugfs_remove_file(hdmi->debugfs);
682f33656e1SLaurent Pinchart
683ac767456SLaurent Pinchart if (hdmi->audio_pdev)
684ac767456SLaurent Pinchart platform_device_unregister(hdmi->audio_pdev);
6859960aa7cSTomi Valkeinen
686ac767456SLaurent Pinchart hdmi4_cec_uninit(&hdmi->core);
687ac767456SLaurent Pinchart hdmi_pll_uninit(&hdmi->pll);
6889960aa7cSTomi Valkeinen }
6899960aa7cSTomi Valkeinen
6909960aa7cSTomi Valkeinen static const struct component_ops hdmi4_component_ops = {
6919960aa7cSTomi Valkeinen .bind = hdmi4_bind,
6929960aa7cSTomi Valkeinen .unbind = hdmi4_unbind,
6939960aa7cSTomi Valkeinen };
6949960aa7cSTomi Valkeinen
6955fc15d98SLaurent Pinchart /* -----------------------------------------------------------------------------
6965fc15d98SLaurent Pinchart * Probe & Remove, Suspend & Resume
6975fc15d98SLaurent Pinchart */
6985fc15d98SLaurent Pinchart
hdmi4_init_output(struct omap_hdmi * hdmi)69927d62452SLaurent Pinchart static int hdmi4_init_output(struct omap_hdmi *hdmi)
7005fc15d98SLaurent Pinchart {
7015fc15d98SLaurent Pinchart struct omap_dss_device *out = &hdmi->output;
70271316556SLaurent Pinchart int r;
7035fc15d98SLaurent Pinchart
704ee34f23aSLaurent Pinchart hdmi4_bridge_init(hdmi);
705ee34f23aSLaurent Pinchart
7065fc15d98SLaurent Pinchart out->dev = &hdmi->pdev->dev;
7075fc15d98SLaurent Pinchart out->id = OMAP_DSS_OUTPUT_HDMI;
7080dbfc396SLaurent Pinchart out->type = OMAP_DISPLAY_TYPE_HDMI;
7095fc15d98SLaurent Pinchart out->name = "hdmi.0";
7105fc15d98SLaurent Pinchart out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
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
hdmi4_uninit_output(struct omap_hdmi * hdmi)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
hdmi4_probe_of(struct omap_hdmi * hdmi)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
hdmi4_probe(struct platform_device * pdev)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
hdmi4_remove(struct platform_device * pdev)827*c2807ecbSUwe Kleine-König static void 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 }
8399960aa7cSTomi Valkeinen
8409960aa7cSTomi Valkeinen static const struct of_device_id hdmi_of_match[] = {
8419960aa7cSTomi Valkeinen { .compatible = "ti,omap4-hdmi", },
8429960aa7cSTomi Valkeinen {},
8439960aa7cSTomi Valkeinen };
8449960aa7cSTomi Valkeinen
845d66c36a3SAndrew F. Davis struct platform_driver omapdss_hdmi4hw_driver = {
8469960aa7cSTomi Valkeinen .probe = hdmi4_probe,
847*c2807ecbSUwe Kleine-König .remove_new = hdmi4_remove,
8489960aa7cSTomi Valkeinen .driver = {
8499960aa7cSTomi Valkeinen .name = "omapdss_hdmi",
8509960aa7cSTomi Valkeinen .of_match_table = hdmi_of_match,
8519960aa7cSTomi Valkeinen .suppress_bind_attrs = true,
8529960aa7cSTomi Valkeinen },
8539960aa7cSTomi Valkeinen };
854