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