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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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