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 * 59960aa7cSTomi Valkeinen * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://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/gpio.h> 249960aa7cSTomi Valkeinen #include <linux/regulator/consumer.h> 259960aa7cSTomi Valkeinen #include <linux/component.h> 26d9e32ecdSTomi Valkeinen #include <linux/of.h> 2709bffa6eSRob Herring #include <linux/of_graph.h> 289960aa7cSTomi Valkeinen #include <sound/omap-hdmi-audio.h> 291897e1a3SHans Verkuil #include <media/cec.h> 309960aa7cSTomi Valkeinen 3132043da7SPeter Ujfalusi #include "omapdss.h" 329960aa7cSTomi Valkeinen #include "hdmi4_core.h" 331897e1a3SHans Verkuil #include "hdmi4_cec.h" 349960aa7cSTomi Valkeinen #include "dss.h" 359960aa7cSTomi Valkeinen #include "hdmi.h" 369960aa7cSTomi Valkeinen 37ac767456SLaurent Pinchart static int hdmi_runtime_get(struct omap_hdmi *hdmi) 389960aa7cSTomi Valkeinen { 399960aa7cSTomi Valkeinen int r; 409960aa7cSTomi Valkeinen 419960aa7cSTomi Valkeinen DSSDBG("hdmi_runtime_get\n"); 429960aa7cSTomi Valkeinen 43ac767456SLaurent Pinchart r = pm_runtime_get_sync(&hdmi->pdev->dev); 449960aa7cSTomi Valkeinen WARN_ON(r < 0); 459960aa7cSTomi Valkeinen if (r < 0) 469960aa7cSTomi Valkeinen return r; 479960aa7cSTomi Valkeinen 489960aa7cSTomi Valkeinen return 0; 499960aa7cSTomi Valkeinen } 509960aa7cSTomi Valkeinen 51ac767456SLaurent Pinchart static void hdmi_runtime_put(struct omap_hdmi *hdmi) 529960aa7cSTomi Valkeinen { 539960aa7cSTomi Valkeinen int r; 549960aa7cSTomi Valkeinen 559960aa7cSTomi Valkeinen DSSDBG("hdmi_runtime_put\n"); 569960aa7cSTomi Valkeinen 57ac767456SLaurent Pinchart r = pm_runtime_put_sync(&hdmi->pdev->dev); 589960aa7cSTomi Valkeinen WARN_ON(r < 0 && r != -ENOSYS); 599960aa7cSTomi Valkeinen } 609960aa7cSTomi Valkeinen 619960aa7cSTomi Valkeinen static irqreturn_t hdmi_irq_handler(int irq, void *data) 629960aa7cSTomi Valkeinen { 63f3096a4aSHans Verkuil struct omap_hdmi *hdmi = data; 64f3096a4aSHans Verkuil struct hdmi_wp_data *wp = &hdmi->wp; 659960aa7cSTomi Valkeinen u32 irqstatus; 669960aa7cSTomi Valkeinen 679960aa7cSTomi Valkeinen irqstatus = hdmi_wp_get_irqstatus(wp); 689960aa7cSTomi Valkeinen hdmi_wp_set_irqstatus(wp, irqstatus); 699960aa7cSTomi Valkeinen 709960aa7cSTomi Valkeinen if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && 719960aa7cSTomi Valkeinen irqstatus & HDMI_IRQ_LINK_DISCONNECT) { 729960aa7cSTomi Valkeinen /* 739960aa7cSTomi Valkeinen * If we get both connect and disconnect interrupts at the same 749960aa7cSTomi Valkeinen * time, turn off the PHY, clear interrupts, and restart, which 759960aa7cSTomi Valkeinen * raises connect interrupt if a cable is connected, or nothing 769960aa7cSTomi Valkeinen * if cable is not connected. 779960aa7cSTomi Valkeinen */ 789960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); 799960aa7cSTomi Valkeinen 809960aa7cSTomi Valkeinen hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | 819960aa7cSTomi Valkeinen HDMI_IRQ_LINK_DISCONNECT); 829960aa7cSTomi Valkeinen 839960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); 849960aa7cSTomi Valkeinen } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { 859960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); 869960aa7cSTomi Valkeinen } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { 879960aa7cSTomi Valkeinen hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); 889960aa7cSTomi Valkeinen } 891897e1a3SHans Verkuil if (irqstatus & HDMI_IRQ_CORE) { 901897e1a3SHans Verkuil u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4); 911897e1a3SHans Verkuil 921897e1a3SHans Verkuil hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4); 931897e1a3SHans Verkuil if (intr4 & 8) 941897e1a3SHans Verkuil hdmi4_cec_irq(&hdmi->core); 951897e1a3SHans Verkuil } 969960aa7cSTomi Valkeinen 979960aa7cSTomi Valkeinen return IRQ_HANDLED; 989960aa7cSTomi Valkeinen } 999960aa7cSTomi Valkeinen 100ac767456SLaurent Pinchart static int hdmi_power_on_core(struct omap_hdmi *hdmi) 1019960aa7cSTomi Valkeinen { 1029960aa7cSTomi Valkeinen int r; 1039960aa7cSTomi Valkeinen 104ac767456SLaurent Pinchart if (hdmi->core.core_pwr_cnt++) 105a141a296SHans Verkuil return 0; 106a141a296SHans Verkuil 107ac767456SLaurent Pinchart r = regulator_enable(hdmi->vdda_reg); 1089960aa7cSTomi Valkeinen if (r) 109a141a296SHans Verkuil goto err_reg_enable; 1109960aa7cSTomi Valkeinen 111ac767456SLaurent Pinchart r = hdmi_runtime_get(hdmi); 1129960aa7cSTomi Valkeinen if (r) 1139960aa7cSTomi Valkeinen goto err_runtime_get; 1149960aa7cSTomi Valkeinen 115ac767456SLaurent Pinchart hdmi4_core_powerdown_disable(&hdmi->core); 1161d54ecf2SHans Verkuil 1179960aa7cSTomi Valkeinen /* Make selection of HDMI in DSS */ 118ac767456SLaurent Pinchart dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK); 1199960aa7cSTomi Valkeinen 120ac767456SLaurent Pinchart hdmi->core_enabled = true; 1219960aa7cSTomi Valkeinen 1229960aa7cSTomi Valkeinen return 0; 1239960aa7cSTomi Valkeinen 1249960aa7cSTomi Valkeinen err_runtime_get: 125ac767456SLaurent Pinchart regulator_disable(hdmi->vdda_reg); 126a141a296SHans Verkuil err_reg_enable: 127ac767456SLaurent Pinchart hdmi->core.core_pwr_cnt--; 1289960aa7cSTomi Valkeinen 1299960aa7cSTomi Valkeinen return r; 1309960aa7cSTomi Valkeinen } 1319960aa7cSTomi Valkeinen 132ac767456SLaurent Pinchart static void hdmi_power_off_core(struct omap_hdmi *hdmi) 1339960aa7cSTomi Valkeinen { 134ac767456SLaurent Pinchart if (--hdmi->core.core_pwr_cnt) 135a141a296SHans Verkuil return; 136a141a296SHans Verkuil 137ac767456SLaurent Pinchart hdmi->core_enabled = false; 1389960aa7cSTomi Valkeinen 139ac767456SLaurent Pinchart hdmi_runtime_put(hdmi); 140ac767456SLaurent Pinchart regulator_disable(hdmi->vdda_reg); 1419960aa7cSTomi Valkeinen } 1429960aa7cSTomi Valkeinen 143ac767456SLaurent Pinchart static int hdmi_power_on_full(struct omap_hdmi *hdmi) 1449960aa7cSTomi Valkeinen { 1459960aa7cSTomi Valkeinen int r; 14695e472daSLaurent Pinchart const struct videomode *vm; 147ac767456SLaurent Pinchart struct hdmi_wp_data *wp = &hdmi->wp; 1489960aa7cSTomi Valkeinen struct dss_pll_clock_info hdmi_cinfo = { 0 }; 149d11e5c82SLaurent Pinchart unsigned int pc; 1509960aa7cSTomi Valkeinen 151ac767456SLaurent Pinchart r = hdmi_power_on_core(hdmi); 1529960aa7cSTomi Valkeinen if (r) 1539960aa7cSTomi Valkeinen return r; 1549960aa7cSTomi Valkeinen 1559960aa7cSTomi Valkeinen /* disable and clear irqs */ 156f3096a4aSHans Verkuil hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE); 157f3096a4aSHans Verkuil hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE); 1589960aa7cSTomi Valkeinen 159ac767456SLaurent Pinchart vm = &hdmi->cfg.vm; 1609960aa7cSTomi Valkeinen 161da11bbbbSPeter Ujfalusi DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive, 162da11bbbbSPeter Ujfalusi vm->vactive); 1639960aa7cSTomi Valkeinen 164da11bbbbSPeter Ujfalusi pc = vm->pixelclock; 165da11bbbbSPeter Ujfalusi if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) 16667d8ffddSTomi Valkeinen pc *= 2; 16767d8ffddSTomi Valkeinen 168c107751dSTomi Valkeinen /* DSS_HDMI_TCLK is bitclk / 10 */ 169c107751dSTomi Valkeinen pc *= 10; 170c107751dSTomi Valkeinen 171ac767456SLaurent Pinchart dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin), 172c17dc0e3STomi Valkeinen pc, &hdmi_cinfo); 1739960aa7cSTomi Valkeinen 174ac767456SLaurent Pinchart r = dss_pll_enable(&hdmi->pll.pll); 1759960aa7cSTomi Valkeinen if (r) { 1769960aa7cSTomi Valkeinen DSSERR("Failed to enable PLL\n"); 1779960aa7cSTomi Valkeinen goto err_pll_enable; 1789960aa7cSTomi Valkeinen } 1799960aa7cSTomi Valkeinen 180ac767456SLaurent Pinchart r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo); 1819960aa7cSTomi Valkeinen if (r) { 1829960aa7cSTomi Valkeinen DSSERR("Failed to configure PLL\n"); 1839960aa7cSTomi Valkeinen goto err_pll_cfg; 1849960aa7cSTomi Valkeinen } 1859960aa7cSTomi Valkeinen 186ac767456SLaurent Pinchart r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco, 1879960aa7cSTomi Valkeinen hdmi_cinfo.clkout[0]); 1889960aa7cSTomi Valkeinen if (r) { 1899960aa7cSTomi Valkeinen DSSDBG("Failed to configure PHY\n"); 1909960aa7cSTomi Valkeinen goto err_phy_cfg; 1919960aa7cSTomi Valkeinen } 1929960aa7cSTomi Valkeinen 1939960aa7cSTomi Valkeinen r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); 1949960aa7cSTomi Valkeinen if (r) 1959960aa7cSTomi Valkeinen goto err_phy_pwr; 1969960aa7cSTomi Valkeinen 197ac767456SLaurent Pinchart hdmi4_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg); 1989960aa7cSTomi Valkeinen 199ac767456SLaurent Pinchart r = dss_mgr_enable(&hdmi->output); 2009960aa7cSTomi Valkeinen if (r) 2019960aa7cSTomi Valkeinen goto err_mgr_enable; 2029960aa7cSTomi Valkeinen 203ac767456SLaurent Pinchart r = hdmi_wp_video_start(&hdmi->wp); 2044e4b53ceSTomi Valkeinen if (r) 2054e4b53ceSTomi Valkeinen goto err_vid_enable; 2064e4b53ceSTomi Valkeinen 2079960aa7cSTomi Valkeinen hdmi_wp_set_irqenable(wp, 2089960aa7cSTomi Valkeinen HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); 2099960aa7cSTomi Valkeinen 2109960aa7cSTomi Valkeinen return 0; 2119960aa7cSTomi Valkeinen 2129960aa7cSTomi Valkeinen err_vid_enable: 213ac767456SLaurent Pinchart dss_mgr_disable(&hdmi->output); 2144e4b53ceSTomi Valkeinen err_mgr_enable: 215ac767456SLaurent Pinchart hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF); 2169960aa7cSTomi Valkeinen err_phy_pwr: 2179960aa7cSTomi Valkeinen err_phy_cfg: 2189960aa7cSTomi Valkeinen err_pll_cfg: 219ac767456SLaurent Pinchart dss_pll_disable(&hdmi->pll.pll); 2209960aa7cSTomi Valkeinen err_pll_enable: 221ac767456SLaurent Pinchart hdmi_power_off_core(hdmi); 2229960aa7cSTomi Valkeinen return -EIO; 2239960aa7cSTomi Valkeinen } 2249960aa7cSTomi Valkeinen 225ac767456SLaurent Pinchart static void hdmi_power_off_full(struct omap_hdmi *hdmi) 2269960aa7cSTomi Valkeinen { 227ac767456SLaurent Pinchart hdmi_wp_clear_irqenable(&hdmi->wp, ~HDMI_IRQ_CORE); 2289960aa7cSTomi Valkeinen 229ac767456SLaurent Pinchart hdmi_wp_video_stop(&hdmi->wp); 2309960aa7cSTomi Valkeinen 231ac767456SLaurent Pinchart dss_mgr_disable(&hdmi->output); 2324e4b53ceSTomi Valkeinen 233ac767456SLaurent Pinchart hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF); 2349960aa7cSTomi Valkeinen 235ac767456SLaurent Pinchart dss_pll_disable(&hdmi->pll.pll); 2369960aa7cSTomi Valkeinen 237ac767456SLaurent Pinchart hdmi_power_off_core(hdmi); 2389960aa7cSTomi Valkeinen } 2399960aa7cSTomi Valkeinen 240ec68cd5aSLaurent Pinchart static void hdmi_display_set_timings(struct omap_dss_device *dssdev, 24141322aa6SLaurent Pinchart const struct drm_display_mode *mode) 2429960aa7cSTomi Valkeinen { 243ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 2449960aa7cSTomi Valkeinen 245ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 2469960aa7cSTomi Valkeinen 24741322aa6SLaurent Pinchart drm_display_mode_to_videomode(mode, &hdmi->cfg.vm); 2489960aa7cSTomi Valkeinen 24941322aa6SLaurent Pinchart dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000); 250ac767456SLaurent Pinchart 251ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 2529960aa7cSTomi Valkeinen } 2539960aa7cSTomi Valkeinen 254f33656e1SLaurent Pinchart static int hdmi_dump_regs(struct seq_file *s, void *p) 2559960aa7cSTomi Valkeinen { 256ac767456SLaurent Pinchart struct omap_hdmi *hdmi = s->private; 2579960aa7cSTomi Valkeinen 258ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 259ac767456SLaurent Pinchart 260ac767456SLaurent Pinchart if (hdmi_runtime_get(hdmi)) { 261ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 262f33656e1SLaurent Pinchart return 0; 2639960aa7cSTomi Valkeinen } 2649960aa7cSTomi Valkeinen 265ac767456SLaurent Pinchart hdmi_wp_dump(&hdmi->wp, s); 266ac767456SLaurent Pinchart hdmi_pll_dump(&hdmi->pll, s); 267ac767456SLaurent Pinchart hdmi_phy_dump(&hdmi->phy, s); 268ac767456SLaurent Pinchart hdmi4_core_dump(&hdmi->core, s); 2699960aa7cSTomi Valkeinen 270ac767456SLaurent Pinchart hdmi_runtime_put(hdmi); 271ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 272f33656e1SLaurent Pinchart return 0; 2739960aa7cSTomi Valkeinen } 2749960aa7cSTomi Valkeinen 275ac767456SLaurent Pinchart static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len) 2769960aa7cSTomi Valkeinen { 2779960aa7cSTomi Valkeinen int r; 2789960aa7cSTomi Valkeinen 279ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 2809960aa7cSTomi Valkeinen 281ac767456SLaurent Pinchart r = hdmi_runtime_get(hdmi); 2829960aa7cSTomi Valkeinen BUG_ON(r); 2839960aa7cSTomi Valkeinen 284ac767456SLaurent Pinchart r = hdmi4_read_edid(&hdmi->core, buf, len); 2859960aa7cSTomi Valkeinen 286ac767456SLaurent Pinchart hdmi_runtime_put(hdmi); 287ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 2889960aa7cSTomi Valkeinen 2899960aa7cSTomi Valkeinen return r; 2909960aa7cSTomi Valkeinen } 2919960aa7cSTomi Valkeinen 2929960aa7cSTomi Valkeinen static void hdmi_start_audio_stream(struct omap_hdmi *hd) 2939960aa7cSTomi Valkeinen { 2949960aa7cSTomi Valkeinen hdmi_wp_audio_enable(&hd->wp, true); 2959960aa7cSTomi Valkeinen hdmi4_audio_start(&hd->core, &hd->wp); 2969960aa7cSTomi Valkeinen } 2979960aa7cSTomi Valkeinen 2989960aa7cSTomi Valkeinen static void hdmi_stop_audio_stream(struct omap_hdmi *hd) 2999960aa7cSTomi Valkeinen { 3009960aa7cSTomi Valkeinen hdmi4_audio_stop(&hd->core, &hd->wp); 3019960aa7cSTomi Valkeinen hdmi_wp_audio_enable(&hd->wp, false); 3029960aa7cSTomi Valkeinen } 3039960aa7cSTomi Valkeinen 30419b4200dSLaurent Pinchart static void hdmi_display_enable(struct omap_dss_device *dssdev) 3059960aa7cSTomi Valkeinen { 306ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 3079960aa7cSTomi Valkeinen unsigned long flags; 30819b4200dSLaurent Pinchart int r; 3099960aa7cSTomi Valkeinen 3109960aa7cSTomi Valkeinen DSSDBG("ENTER hdmi_display_enable\n"); 3119960aa7cSTomi Valkeinen 312ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 3139960aa7cSTomi Valkeinen 314ac767456SLaurent Pinchart r = hdmi_power_on_full(hdmi); 3159960aa7cSTomi Valkeinen if (r) { 3169960aa7cSTomi Valkeinen DSSERR("failed to power on device\n"); 31719b4200dSLaurent Pinchart goto done; 3189960aa7cSTomi Valkeinen } 3199960aa7cSTomi Valkeinen 320ac767456SLaurent Pinchart if (hdmi->audio_configured) { 321ac767456SLaurent Pinchart r = hdmi4_audio_config(&hdmi->core, &hdmi->wp, 322ac767456SLaurent Pinchart &hdmi->audio_config, 323ac767456SLaurent Pinchart hdmi->cfg.vm.pixelclock); 3249960aa7cSTomi Valkeinen if (r) { 3259960aa7cSTomi Valkeinen DSSERR("Error restoring audio configuration: %d", r); 326ac767456SLaurent Pinchart hdmi->audio_abort_cb(&hdmi->pdev->dev); 327ac767456SLaurent Pinchart hdmi->audio_configured = false; 3289960aa7cSTomi Valkeinen } 3299960aa7cSTomi Valkeinen } 3309960aa7cSTomi Valkeinen 331ac767456SLaurent Pinchart spin_lock_irqsave(&hdmi->audio_playing_lock, flags); 332ac767456SLaurent Pinchart if (hdmi->audio_configured && hdmi->audio_playing) 333ac767456SLaurent Pinchart hdmi_start_audio_stream(hdmi); 334ac767456SLaurent Pinchart hdmi->display_enabled = true; 335ac767456SLaurent Pinchart spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); 3369960aa7cSTomi Valkeinen 33719b4200dSLaurent Pinchart done: 338ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 3399960aa7cSTomi Valkeinen } 3409960aa7cSTomi Valkeinen 3419960aa7cSTomi Valkeinen static void hdmi_display_disable(struct omap_dss_device *dssdev) 3429960aa7cSTomi Valkeinen { 343ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 3449960aa7cSTomi Valkeinen unsigned long flags; 3459960aa7cSTomi Valkeinen 3469960aa7cSTomi Valkeinen DSSDBG("Enter hdmi_display_disable\n"); 3479960aa7cSTomi Valkeinen 348ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 3499960aa7cSTomi Valkeinen 350ac767456SLaurent Pinchart spin_lock_irqsave(&hdmi->audio_playing_lock, flags); 351ac767456SLaurent Pinchart hdmi_stop_audio_stream(hdmi); 352ac767456SLaurent Pinchart hdmi->display_enabled = false; 353ac767456SLaurent Pinchart spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); 3549960aa7cSTomi Valkeinen 355ac767456SLaurent Pinchart hdmi_power_off_full(hdmi); 3569960aa7cSTomi Valkeinen 357ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 3589960aa7cSTomi Valkeinen } 3599960aa7cSTomi Valkeinen 360ac767456SLaurent Pinchart int hdmi4_core_enable(struct hdmi_core_data *core) 3619960aa7cSTomi Valkeinen { 362ac767456SLaurent Pinchart struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); 3639960aa7cSTomi Valkeinen int r = 0; 3649960aa7cSTomi Valkeinen 3655bebbbfeSHans Verkuil DSSDBG("ENTER omapdss_hdmi4_core_enable\n"); 3669960aa7cSTomi Valkeinen 367ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 3689960aa7cSTomi Valkeinen 369ac767456SLaurent Pinchart r = hdmi_power_on_core(hdmi); 3709960aa7cSTomi Valkeinen if (r) { 3719960aa7cSTomi Valkeinen DSSERR("failed to power on device\n"); 3729960aa7cSTomi Valkeinen goto err0; 3739960aa7cSTomi Valkeinen } 3749960aa7cSTomi Valkeinen 375ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 3769960aa7cSTomi Valkeinen return 0; 3779960aa7cSTomi Valkeinen 3789960aa7cSTomi Valkeinen err0: 379ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 3809960aa7cSTomi Valkeinen return r; 3819960aa7cSTomi Valkeinen } 3829960aa7cSTomi Valkeinen 383ac767456SLaurent Pinchart void hdmi4_core_disable(struct hdmi_core_data *core) 3849960aa7cSTomi Valkeinen { 385ac767456SLaurent Pinchart struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); 386ac767456SLaurent Pinchart 3875bebbbfeSHans Verkuil DSSDBG("Enter omapdss_hdmi4_core_disable\n"); 3889960aa7cSTomi Valkeinen 389ac767456SLaurent Pinchart mutex_lock(&hdmi->lock); 3909960aa7cSTomi Valkeinen 391ac767456SLaurent Pinchart hdmi_power_off_core(hdmi); 3929960aa7cSTomi Valkeinen 393ac767456SLaurent Pinchart mutex_unlock(&hdmi->lock); 3949960aa7cSTomi Valkeinen } 3959960aa7cSTomi Valkeinen 396511afb44SLaurent Pinchart static int hdmi_connect(struct omap_dss_device *src, 3979960aa7cSTomi Valkeinen struct omap_dss_device *dst) 3989960aa7cSTomi Valkeinen { 399f8a8eabbSLaurent Pinchart return omapdss_device_connect(dst->dss, dst, dst->next); 40071316556SLaurent Pinchart } 40171316556SLaurent Pinchart 402511afb44SLaurent Pinchart static void hdmi_disconnect(struct omap_dss_device *src, 4039960aa7cSTomi Valkeinen struct omap_dss_device *dst) 4049960aa7cSTomi Valkeinen { 405511afb44SLaurent Pinchart omapdss_device_disconnect(dst, dst->next); 4069960aa7cSTomi Valkeinen } 4079960aa7cSTomi Valkeinen 4089960aa7cSTomi Valkeinen static int hdmi_read_edid(struct omap_dss_device *dssdev, 4099960aa7cSTomi Valkeinen u8 *edid, int len) 4109960aa7cSTomi Valkeinen { 411ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 4129960aa7cSTomi Valkeinen bool need_enable; 4139960aa7cSTomi Valkeinen int r; 4149960aa7cSTomi Valkeinen 415ac767456SLaurent Pinchart need_enable = hdmi->core_enabled == false; 4169960aa7cSTomi Valkeinen 4179960aa7cSTomi Valkeinen if (need_enable) { 418ac767456SLaurent Pinchart r = hdmi4_core_enable(&hdmi->core); 4199960aa7cSTomi Valkeinen if (r) 4209960aa7cSTomi Valkeinen return r; 4219960aa7cSTomi Valkeinen } 4229960aa7cSTomi Valkeinen 423ac767456SLaurent Pinchart r = read_edid(hdmi, edid, len); 4241897e1a3SHans Verkuil if (r >= 256) 425ac767456SLaurent Pinchart hdmi4_cec_set_phys_addr(&hdmi->core, 4261897e1a3SHans Verkuil cec_get_edid_phys_addr(edid, r, NULL)); 4271897e1a3SHans Verkuil else 428ac767456SLaurent Pinchart hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); 4299960aa7cSTomi Valkeinen if (need_enable) 430ac767456SLaurent Pinchart hdmi4_core_disable(&hdmi->core); 4319960aa7cSTomi Valkeinen 4329960aa7cSTomi Valkeinen return r; 4339960aa7cSTomi Valkeinen } 4349960aa7cSTomi Valkeinen 435019114efSHans Verkuil static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) 436019114efSHans Verkuil { 437ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 438ac767456SLaurent Pinchart 439ac767456SLaurent Pinchart hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); 440019114efSHans Verkuil } 441019114efSHans Verkuil 4429960aa7cSTomi Valkeinen static int hdmi_set_infoframe(struct omap_dss_device *dssdev, 4439960aa7cSTomi Valkeinen const struct hdmi_avi_infoframe *avi) 4449960aa7cSTomi Valkeinen { 445ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 446ac767456SLaurent Pinchart 447ac767456SLaurent Pinchart hdmi->cfg.infoframe = *avi; 4489960aa7cSTomi Valkeinen return 0; 4499960aa7cSTomi Valkeinen } 4509960aa7cSTomi Valkeinen 4519960aa7cSTomi Valkeinen static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, 4529960aa7cSTomi Valkeinen bool hdmi_mode) 4539960aa7cSTomi Valkeinen { 454ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); 455ac767456SLaurent Pinchart 456ac767456SLaurent Pinchart hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; 4579960aa7cSTomi Valkeinen return 0; 4589960aa7cSTomi Valkeinen } 4599960aa7cSTomi Valkeinen 460b93109d7SLaurent Pinchart static const struct omap_dss_device_ops hdmi_ops = { 4619960aa7cSTomi Valkeinen .connect = hdmi_connect, 4629960aa7cSTomi Valkeinen .disconnect = hdmi_disconnect, 4639960aa7cSTomi Valkeinen 4649960aa7cSTomi Valkeinen .enable = hdmi_display_enable, 4659960aa7cSTomi Valkeinen .disable = hdmi_display_disable, 4669960aa7cSTomi Valkeinen 467ec68cd5aSLaurent Pinchart .set_timings = hdmi_display_set_timings, 4689960aa7cSTomi Valkeinen 4699960aa7cSTomi Valkeinen .read_edid = hdmi_read_edid, 47083910ad3SLaurent Pinchart 47183910ad3SLaurent Pinchart .hdmi = { 472019114efSHans Verkuil .lost_hotplug = hdmi_lost_hotplug, 4739960aa7cSTomi Valkeinen .set_infoframe = hdmi_set_infoframe, 4749960aa7cSTomi Valkeinen .set_hdmi_mode = hdmi_set_hdmi_mode, 475b93109d7SLaurent Pinchart }, 4769960aa7cSTomi Valkeinen }; 4779960aa7cSTomi Valkeinen 4785fc15d98SLaurent Pinchart /* ----------------------------------------------------------------------------- 4795fc15d98SLaurent Pinchart * Audio Callbacks 4805fc15d98SLaurent Pinchart */ 4819960aa7cSTomi Valkeinen 4829960aa7cSTomi Valkeinen static int hdmi_audio_startup(struct device *dev, 4839960aa7cSTomi Valkeinen void (*abort_cb)(struct device *dev)) 4849960aa7cSTomi Valkeinen { 4859960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev); 4869960aa7cSTomi Valkeinen 4879960aa7cSTomi Valkeinen mutex_lock(&hd->lock); 4889960aa7cSTomi Valkeinen 489c1899cb3SJyri Sarha WARN_ON(hd->audio_abort_cb != NULL); 4909960aa7cSTomi Valkeinen 4919960aa7cSTomi Valkeinen hd->audio_abort_cb = abort_cb; 4929960aa7cSTomi Valkeinen 4939960aa7cSTomi Valkeinen mutex_unlock(&hd->lock); 4949960aa7cSTomi Valkeinen 495c1899cb3SJyri Sarha return 0; 4969960aa7cSTomi Valkeinen } 4979960aa7cSTomi Valkeinen 4989960aa7cSTomi Valkeinen static int hdmi_audio_shutdown(struct device *dev) 4999960aa7cSTomi Valkeinen { 5009960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev); 5019960aa7cSTomi Valkeinen 5029960aa7cSTomi Valkeinen mutex_lock(&hd->lock); 5039960aa7cSTomi Valkeinen hd->audio_abort_cb = NULL; 5049960aa7cSTomi Valkeinen hd->audio_configured = false; 5059960aa7cSTomi Valkeinen hd->audio_playing = false; 5069960aa7cSTomi Valkeinen mutex_unlock(&hd->lock); 5079960aa7cSTomi Valkeinen 5089960aa7cSTomi Valkeinen return 0; 5099960aa7cSTomi Valkeinen } 5109960aa7cSTomi Valkeinen 5119960aa7cSTomi Valkeinen static int hdmi_audio_start(struct device *dev) 5129960aa7cSTomi Valkeinen { 5139960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev); 5149960aa7cSTomi Valkeinen unsigned long flags; 5159960aa7cSTomi Valkeinen 5169960aa7cSTomi Valkeinen spin_lock_irqsave(&hd->audio_playing_lock, flags); 5179960aa7cSTomi Valkeinen 518c1899cb3SJyri Sarha if (hd->display_enabled) { 519c1899cb3SJyri Sarha if (!hdmi_mode_has_audio(&hd->cfg)) 520c1899cb3SJyri Sarha DSSERR("%s: Video mode does not support audio\n", 521c1899cb3SJyri Sarha __func__); 5229960aa7cSTomi Valkeinen hdmi_start_audio_stream(hd); 523c1899cb3SJyri Sarha } 5249960aa7cSTomi Valkeinen hd->audio_playing = true; 5259960aa7cSTomi Valkeinen 5269960aa7cSTomi Valkeinen spin_unlock_irqrestore(&hd->audio_playing_lock, flags); 5279960aa7cSTomi Valkeinen return 0; 5289960aa7cSTomi Valkeinen } 5299960aa7cSTomi Valkeinen 5309960aa7cSTomi Valkeinen static void hdmi_audio_stop(struct device *dev) 5319960aa7cSTomi Valkeinen { 5329960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev); 5339960aa7cSTomi Valkeinen unsigned long flags; 5349960aa7cSTomi Valkeinen 5359960aa7cSTomi Valkeinen WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); 5369960aa7cSTomi Valkeinen 5379960aa7cSTomi Valkeinen spin_lock_irqsave(&hd->audio_playing_lock, flags); 5389960aa7cSTomi Valkeinen 5399960aa7cSTomi Valkeinen if (hd->display_enabled) 5409960aa7cSTomi Valkeinen hdmi_stop_audio_stream(hd); 5419960aa7cSTomi Valkeinen hd->audio_playing = false; 5429960aa7cSTomi Valkeinen 5439960aa7cSTomi Valkeinen spin_unlock_irqrestore(&hd->audio_playing_lock, flags); 5449960aa7cSTomi Valkeinen } 5459960aa7cSTomi Valkeinen 5469960aa7cSTomi Valkeinen static int hdmi_audio_config(struct device *dev, 5479960aa7cSTomi Valkeinen struct omap_dss_audio *dss_audio) 5489960aa7cSTomi Valkeinen { 5499960aa7cSTomi Valkeinen struct omap_hdmi *hd = dev_get_drvdata(dev); 55077eeac24STomi Valkeinen int ret = 0; 5519960aa7cSTomi Valkeinen 5529960aa7cSTomi Valkeinen mutex_lock(&hd->lock); 5539960aa7cSTomi Valkeinen 554c1899cb3SJyri Sarha if (hd->display_enabled) { 555c1899cb3SJyri Sarha ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, 556c1899cb3SJyri Sarha hd->cfg.vm.pixelclock); 557c1899cb3SJyri Sarha if (ret) 5589960aa7cSTomi Valkeinen goto out; 5599960aa7cSTomi Valkeinen } 5609960aa7cSTomi Valkeinen 5619960aa7cSTomi Valkeinen hd->audio_configured = true; 5629960aa7cSTomi Valkeinen hd->audio_config = *dss_audio; 5639960aa7cSTomi Valkeinen out: 5649960aa7cSTomi Valkeinen mutex_unlock(&hd->lock); 5659960aa7cSTomi Valkeinen 5669960aa7cSTomi Valkeinen return ret; 5679960aa7cSTomi Valkeinen } 5689960aa7cSTomi Valkeinen 5699960aa7cSTomi Valkeinen static const struct omap_hdmi_audio_ops hdmi_audio_ops = { 5709960aa7cSTomi Valkeinen .audio_startup = hdmi_audio_startup, 5719960aa7cSTomi Valkeinen .audio_shutdown = hdmi_audio_shutdown, 5729960aa7cSTomi Valkeinen .audio_start = hdmi_audio_start, 5739960aa7cSTomi Valkeinen .audio_stop = hdmi_audio_stop, 5749960aa7cSTomi Valkeinen .audio_config = hdmi_audio_config, 5759960aa7cSTomi Valkeinen }; 5769960aa7cSTomi Valkeinen 577ac767456SLaurent Pinchart static int hdmi_audio_register(struct omap_hdmi *hdmi) 5789960aa7cSTomi Valkeinen { 5799960aa7cSTomi Valkeinen struct omap_hdmi_audio_pdata pdata = { 580ac767456SLaurent Pinchart .dev = &hdmi->pdev->dev, 581d20fa5a0SLaurent Pinchart .version = 4, 582ac767456SLaurent Pinchart .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp), 5839960aa7cSTomi Valkeinen .ops = &hdmi_audio_ops, 5849960aa7cSTomi Valkeinen }; 5859960aa7cSTomi Valkeinen 586ac767456SLaurent Pinchart hdmi->audio_pdev = platform_device_register_data( 587ac767456SLaurent Pinchart &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, 5889960aa7cSTomi Valkeinen &pdata, sizeof(pdata)); 5899960aa7cSTomi Valkeinen 590ac767456SLaurent Pinchart if (IS_ERR(hdmi->audio_pdev)) 591ac767456SLaurent Pinchart return PTR_ERR(hdmi->audio_pdev); 5929960aa7cSTomi Valkeinen 5939960aa7cSTomi Valkeinen return 0; 5949960aa7cSTomi Valkeinen } 5959960aa7cSTomi Valkeinen 5965fc15d98SLaurent Pinchart /* ----------------------------------------------------------------------------- 5975fc15d98SLaurent Pinchart * Component Bind & Unbind 5985fc15d98SLaurent Pinchart */ 5995fc15d98SLaurent Pinchart 6009960aa7cSTomi Valkeinen static int hdmi4_bind(struct device *dev, struct device *master, void *data) 6019960aa7cSTomi Valkeinen { 6027b295257SLaurent Pinchart struct dss_device *dss = dss_get_device(master); 6035fc15d98SLaurent Pinchart struct omap_hdmi *hdmi = dev_get_drvdata(dev); 6049960aa7cSTomi Valkeinen int r; 6059960aa7cSTomi Valkeinen 606ac767456SLaurent Pinchart hdmi->dss = dss; 6079960aa7cSTomi Valkeinen 608f8523b64SLaurent Pinchart r = hdmi_runtime_get(hdmi); 6099960aa7cSTomi Valkeinen if (r) 6105fc15d98SLaurent Pinchart return r; 6119960aa7cSTomi Valkeinen 612f8523b64SLaurent Pinchart r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp); 613f8523b64SLaurent Pinchart if (r) 614f8523b64SLaurent Pinchart goto err_runtime_put; 615f8523b64SLaurent Pinchart 6165fc15d98SLaurent Pinchart r = hdmi4_cec_init(hdmi->pdev, &hdmi->core, &hdmi->wp); 6179960aa7cSTomi Valkeinen if (r) 6185fc15d98SLaurent Pinchart goto err_pll_uninit; 6199960aa7cSTomi Valkeinen 620ac767456SLaurent Pinchart r = hdmi_audio_register(hdmi); 6219960aa7cSTomi Valkeinen if (r) { 6229960aa7cSTomi Valkeinen DSSERR("Registering HDMI audio failed\n"); 6235fc15d98SLaurent Pinchart goto err_cec_uninit; 6249960aa7cSTomi Valkeinen } 6259960aa7cSTomi Valkeinen 626ac767456SLaurent Pinchart hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs, 627ac767456SLaurent Pinchart hdmi); 6289960aa7cSTomi Valkeinen 629f8523b64SLaurent Pinchart hdmi_runtime_put(hdmi); 630f8523b64SLaurent Pinchart 6319960aa7cSTomi Valkeinen return 0; 632ac767456SLaurent Pinchart 6335fc15d98SLaurent Pinchart err_cec_uninit: 6345fc15d98SLaurent Pinchart hdmi4_cec_uninit(&hdmi->core); 6355fc15d98SLaurent Pinchart err_pll_uninit: 636ac767456SLaurent Pinchart hdmi_pll_uninit(&hdmi->pll); 637f8523b64SLaurent Pinchart err_runtime_put: 638f8523b64SLaurent Pinchart hdmi_runtime_put(hdmi); 6399960aa7cSTomi Valkeinen return r; 6409960aa7cSTomi Valkeinen } 6419960aa7cSTomi Valkeinen 6429960aa7cSTomi Valkeinen static void hdmi4_unbind(struct device *dev, struct device *master, void *data) 6439960aa7cSTomi Valkeinen { 644ac767456SLaurent Pinchart struct omap_hdmi *hdmi = dev_get_drvdata(dev); 6459960aa7cSTomi Valkeinen 646ac767456SLaurent Pinchart dss_debugfs_remove_file(hdmi->debugfs); 647f33656e1SLaurent Pinchart 648ac767456SLaurent Pinchart if (hdmi->audio_pdev) 649ac767456SLaurent Pinchart platform_device_unregister(hdmi->audio_pdev); 6509960aa7cSTomi Valkeinen 651ac767456SLaurent Pinchart hdmi4_cec_uninit(&hdmi->core); 652ac767456SLaurent Pinchart hdmi_pll_uninit(&hdmi->pll); 6539960aa7cSTomi Valkeinen } 6549960aa7cSTomi Valkeinen 6559960aa7cSTomi Valkeinen static const struct component_ops hdmi4_component_ops = { 6569960aa7cSTomi Valkeinen .bind = hdmi4_bind, 6579960aa7cSTomi Valkeinen .unbind = hdmi4_unbind, 6589960aa7cSTomi Valkeinen }; 6599960aa7cSTomi Valkeinen 6605fc15d98SLaurent Pinchart /* ----------------------------------------------------------------------------- 6615fc15d98SLaurent Pinchart * Probe & Remove, Suspend & Resume 6625fc15d98SLaurent Pinchart */ 6635fc15d98SLaurent Pinchart 66427d62452SLaurent Pinchart static int hdmi4_init_output(struct omap_hdmi *hdmi) 6655fc15d98SLaurent Pinchart { 6665fc15d98SLaurent Pinchart struct omap_dss_device *out = &hdmi->output; 66771316556SLaurent Pinchart int r; 6685fc15d98SLaurent Pinchart 6695fc15d98SLaurent Pinchart out->dev = &hdmi->pdev->dev; 6705fc15d98SLaurent Pinchart out->id = OMAP_DSS_OUTPUT_HDMI; 6710dbfc396SLaurent Pinchart out->type = OMAP_DISPLAY_TYPE_HDMI; 6725fc15d98SLaurent Pinchart out->name = "hdmi.0"; 6735fc15d98SLaurent Pinchart out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; 6745fc15d98SLaurent Pinchart out->ops = &hdmi_ops; 6755fc15d98SLaurent Pinchart out->owner = THIS_MODULE; 676*c83fefd7SLaurent Pinchart out->of_port = 0; 67790279e95SLaurent Pinchart out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; 6785fc15d98SLaurent Pinchart 679d17eb453SLaurent Pinchart r = omapdss_device_init_output(out); 680d17eb453SLaurent Pinchart if (r < 0) 68171316556SLaurent Pinchart return r; 68271316556SLaurent Pinchart 6835fc15d98SLaurent Pinchart omapdss_device_register(out); 68427d62452SLaurent Pinchart 68527d62452SLaurent Pinchart return 0; 6865fc15d98SLaurent Pinchart } 6875fc15d98SLaurent Pinchart 6885fc15d98SLaurent Pinchart static void hdmi4_uninit_output(struct omap_hdmi *hdmi) 6895fc15d98SLaurent Pinchart { 6905fc15d98SLaurent Pinchart struct omap_dss_device *out = &hdmi->output; 6915fc15d98SLaurent Pinchart 6925fc15d98SLaurent Pinchart omapdss_device_unregister(out); 693d17eb453SLaurent Pinchart omapdss_device_cleanup_output(out); 6945fc15d98SLaurent Pinchart } 6955fc15d98SLaurent Pinchart 6965fc15d98SLaurent Pinchart static int hdmi4_probe_of(struct omap_hdmi *hdmi) 6975fc15d98SLaurent Pinchart { 6985fc15d98SLaurent Pinchart struct platform_device *pdev = hdmi->pdev; 6995fc15d98SLaurent Pinchart struct device_node *node = pdev->dev.of_node; 7005fc15d98SLaurent Pinchart struct device_node *ep; 7015fc15d98SLaurent Pinchart int r; 7025fc15d98SLaurent Pinchart 7035fc15d98SLaurent Pinchart ep = of_graph_get_endpoint_by_regs(node, 0, 0); 7045fc15d98SLaurent Pinchart if (!ep) 7055fc15d98SLaurent Pinchart return 0; 7065fc15d98SLaurent Pinchart 7075fc15d98SLaurent Pinchart r = hdmi_parse_lanes_of(pdev, ep, &hdmi->phy); 7085fc15d98SLaurent Pinchart of_node_put(ep); 7095fc15d98SLaurent Pinchart return r; 7105fc15d98SLaurent Pinchart } 7115fc15d98SLaurent Pinchart 7129960aa7cSTomi Valkeinen static int hdmi4_probe(struct platform_device *pdev) 7139960aa7cSTomi Valkeinen { 7145fc15d98SLaurent Pinchart struct omap_hdmi *hdmi; 7155fc15d98SLaurent Pinchart int irq; 7165fc15d98SLaurent Pinchart int r; 7175fc15d98SLaurent Pinchart 7185fc15d98SLaurent Pinchart hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); 7195fc15d98SLaurent Pinchart if (!hdmi) 7205fc15d98SLaurent Pinchart return -ENOMEM; 7215fc15d98SLaurent Pinchart 7225fc15d98SLaurent Pinchart hdmi->pdev = pdev; 7235fc15d98SLaurent Pinchart 7245fc15d98SLaurent Pinchart dev_set_drvdata(&pdev->dev, hdmi); 7255fc15d98SLaurent Pinchart 7265fc15d98SLaurent Pinchart mutex_init(&hdmi->lock); 7275fc15d98SLaurent Pinchart spin_lock_init(&hdmi->audio_playing_lock); 7285fc15d98SLaurent Pinchart 7295fc15d98SLaurent Pinchart r = hdmi4_probe_of(hdmi); 7305fc15d98SLaurent Pinchart if (r) 7315fc15d98SLaurent Pinchart goto err_free; 7325fc15d98SLaurent Pinchart 7335fc15d98SLaurent Pinchart r = hdmi_wp_init(pdev, &hdmi->wp, 4); 7345fc15d98SLaurent Pinchart if (r) 7355fc15d98SLaurent Pinchart goto err_free; 7365fc15d98SLaurent Pinchart 7375fc15d98SLaurent Pinchart r = hdmi_phy_init(pdev, &hdmi->phy, 4); 7385fc15d98SLaurent Pinchart if (r) 7395fc15d98SLaurent Pinchart goto err_free; 7405fc15d98SLaurent Pinchart 7415fc15d98SLaurent Pinchart r = hdmi4_core_init(pdev, &hdmi->core); 7425fc15d98SLaurent Pinchart if (r) 7435fc15d98SLaurent Pinchart goto err_free; 7445fc15d98SLaurent Pinchart 7455fc15d98SLaurent Pinchart irq = platform_get_irq(pdev, 0); 7465fc15d98SLaurent Pinchart if (irq < 0) { 7475fc15d98SLaurent Pinchart DSSERR("platform_get_irq failed\n"); 7485fc15d98SLaurent Pinchart r = -ENODEV; 7495fc15d98SLaurent Pinchart goto err_free; 7505fc15d98SLaurent Pinchart } 7515fc15d98SLaurent Pinchart 7525fc15d98SLaurent Pinchart r = devm_request_threaded_irq(&pdev->dev, irq, 7535fc15d98SLaurent Pinchart NULL, hdmi_irq_handler, 7545fc15d98SLaurent Pinchart IRQF_ONESHOT, "OMAP HDMI", hdmi); 7555fc15d98SLaurent Pinchart if (r) { 7565fc15d98SLaurent Pinchart DSSERR("HDMI IRQ request failed\n"); 7575fc15d98SLaurent Pinchart goto err_free; 7585fc15d98SLaurent Pinchart } 7595fc15d98SLaurent Pinchart 7608a36357aSLaurent Pinchart hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); 7618a36357aSLaurent Pinchart if (IS_ERR(hdmi->vdda_reg)) { 7628a36357aSLaurent Pinchart r = PTR_ERR(hdmi->vdda_reg); 7638a36357aSLaurent Pinchart if (r != -EPROBE_DEFER) 7648a36357aSLaurent Pinchart DSSERR("can't get VDDA regulator\n"); 7658a36357aSLaurent Pinchart goto err_free; 7668a36357aSLaurent Pinchart } 7678a36357aSLaurent Pinchart 7685fc15d98SLaurent Pinchart pm_runtime_enable(&pdev->dev); 7695fc15d98SLaurent Pinchart 77027d62452SLaurent Pinchart r = hdmi4_init_output(hdmi); 77127d62452SLaurent Pinchart if (r) 77227d62452SLaurent Pinchart goto err_pm_disable; 7735fc15d98SLaurent Pinchart 7745fc15d98SLaurent Pinchart r = component_add(&pdev->dev, &hdmi4_component_ops); 7755fc15d98SLaurent Pinchart if (r) 7765fc15d98SLaurent Pinchart goto err_uninit_output; 7775fc15d98SLaurent Pinchart 7785fc15d98SLaurent Pinchart return 0; 7795fc15d98SLaurent Pinchart 7805fc15d98SLaurent Pinchart err_uninit_output: 7815fc15d98SLaurent Pinchart hdmi4_uninit_output(hdmi); 78227d62452SLaurent Pinchart err_pm_disable: 7835fc15d98SLaurent Pinchart pm_runtime_disable(&pdev->dev); 7845fc15d98SLaurent Pinchart err_free: 7855fc15d98SLaurent Pinchart kfree(hdmi); 7865fc15d98SLaurent Pinchart return r; 7879960aa7cSTomi Valkeinen } 7889960aa7cSTomi Valkeinen 7899960aa7cSTomi Valkeinen static int hdmi4_remove(struct platform_device *pdev) 7909960aa7cSTomi Valkeinen { 7915fc15d98SLaurent Pinchart struct omap_hdmi *hdmi = platform_get_drvdata(pdev); 7925fc15d98SLaurent Pinchart 7939960aa7cSTomi Valkeinen component_del(&pdev->dev, &hdmi4_component_ops); 7945fc15d98SLaurent Pinchart 7955fc15d98SLaurent Pinchart hdmi4_uninit_output(hdmi); 7965fc15d98SLaurent Pinchart 7975fc15d98SLaurent Pinchart pm_runtime_disable(&pdev->dev); 7985fc15d98SLaurent Pinchart 7995fc15d98SLaurent Pinchart kfree(hdmi); 8009960aa7cSTomi Valkeinen return 0; 8019960aa7cSTomi Valkeinen } 8029960aa7cSTomi Valkeinen 8039960aa7cSTomi Valkeinen static const struct of_device_id hdmi_of_match[] = { 8049960aa7cSTomi Valkeinen { .compatible = "ti,omap4-hdmi", }, 8059960aa7cSTomi Valkeinen {}, 8069960aa7cSTomi Valkeinen }; 8079960aa7cSTomi Valkeinen 808d66c36a3SAndrew F. Davis struct platform_driver omapdss_hdmi4hw_driver = { 8099960aa7cSTomi Valkeinen .probe = hdmi4_probe, 8109960aa7cSTomi Valkeinen .remove = hdmi4_remove, 8119960aa7cSTomi Valkeinen .driver = { 8129960aa7cSTomi Valkeinen .name = "omapdss_hdmi", 8139960aa7cSTomi Valkeinen .of_match_table = hdmi_of_match, 8149960aa7cSTomi Valkeinen .suppress_bind_attrs = true, 8159960aa7cSTomi Valkeinen }, 8169960aa7cSTomi Valkeinen }; 817