diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 2b0a2aac8aed..44a885b92825 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -70,6 +70,8 @@ static struct { int ls_oe_gpio; int hpd_gpio; + bool core_enabled; + struct omap_dss_device output; } hdmi; @@ -515,8 +517,10 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) { int r; - gpio_set_value(hdmi.ct_cp_hpd_gpio, 1); - gpio_set_value(hdmi.ls_oe_gpio, 1); + if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) + gpio_set_value(hdmi.ct_cp_hpd_gpio, 1); + if (gpio_is_valid(hdmi.ls_oe_gpio)) + gpio_set_value(hdmi.ls_oe_gpio, 1); /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */ udelay(300); @@ -532,22 +536,30 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) /* Make selection of HDMI in DSS */ dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); + hdmi.core_enabled = true; + return 0; err_runtime_get: regulator_disable(hdmi.vdda_hdmi_dac_reg); err_vdac_enable: - gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); - gpio_set_value(hdmi.ls_oe_gpio, 0); + if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) + gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); + if (gpio_is_valid(hdmi.ls_oe_gpio)) + gpio_set_value(hdmi.ls_oe_gpio, 0); return r; } static void hdmi_power_off_core(struct omap_dss_device *dssdev) { + hdmi.core_enabled = false; + hdmi_runtime_put(); regulator_disable(hdmi.vdda_hdmi_dac_reg); - gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); - gpio_set_value(hdmi.ls_oe_gpio, 0); + if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) + gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); + if (gpio_is_valid(hdmi.ls_oe_gpio)) + gpio_set_value(hdmi.ls_oe_gpio, 0); } static int hdmi_power_on_full(struct omap_dss_device *dssdev) @@ -662,6 +674,18 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev, mutex_unlock(&hdmi.lock); } +static void omapdss_hdmi_display_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + const struct hdmi_config *cfg; + + cfg = hdmi_get_timings(); + if (cfg == NULL) + cfg = &vesa_timings[0]; + + memcpy(timings, &cfg->timings, sizeof(cfg->timings)); +} + static void hdmi_dump_regs(struct seq_file *s) { mutex_lock(&hdmi.lock); @@ -1025,6 +1049,199 @@ static int hdmi_probe_pdata(struct platform_device *pdev) return 0; } +static int hdmi_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct omap_overlay_manager *mgr; + int r; + + dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version()); + + r = hdmi_init_regulator(); + if (r) + return r; + + mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); + if (!mgr) + return -ENODEV; + + r = dss_mgr_connect(mgr, dssdev); + if (r) + return r; + + r = omapdss_output_set_device(dssdev, dst); + if (r) { + DSSERR("failed to connect output to new device: %s\n", + dst->name); + dss_mgr_disconnect(mgr, dssdev); + return r; + } + + return 0; +} + +static void hdmi_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + WARN_ON(dst != dssdev->device); + + if (dst != dssdev->device) + return; + + omapdss_output_unset_device(dssdev); + + if (dssdev->manager) + dss_mgr_disconnect(dssdev->manager, dssdev); +} + +static int hdmi_read_edid(struct omap_dss_device *dssdev, + u8 *edid, int len) +{ + bool need_enable; + int r; + + need_enable = hdmi.core_enabled == false; + + if (need_enable) { + r = omapdss_hdmi_core_enable(dssdev); + if (r) + return r; + } + + r = omapdss_hdmi_read_edid(edid, len); + + if (need_enable) + omapdss_hdmi_core_disable(dssdev); + + return r; +} + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev) +{ + int r; + + mutex_lock(&hdmi.lock); + + if (!hdmi_mode_has_audio()) { + r = -EPERM; + goto err; + } + + r = hdmi_audio_enable(); + if (r) + goto err; + + mutex_unlock(&hdmi.lock); + return 0; + +err: + mutex_unlock(&hdmi.lock); + return r; +} + +static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev) +{ + hdmi_audio_disable(); +} + +static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev) +{ + return hdmi_audio_start(); +} + +static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev) +{ + hdmi_audio_stop(); +} + +static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev) +{ + bool r; + + mutex_lock(&hdmi.lock); + + r = hdmi_mode_has_audio(); + + mutex_unlock(&hdmi.lock); + return r; +} + +static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + int r; + + mutex_lock(&hdmi.lock); + + if (!hdmi_mode_has_audio()) { + r = -EPERM; + goto err; + } + + r = hdmi_audio_config(audio); + if (r) + goto err; + + mutex_unlock(&hdmi.lock); + return 0; + +err: + mutex_unlock(&hdmi.lock); + return r; +} +#else +static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev) +{ +} + +static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev) +{ +} + +static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev) +{ + return false; +} + +static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + return -EPERM; +} +#endif + +static const struct omapdss_hdmi_ops hdmi_ops = { + .connect = hdmi_connect, + .disconnect = hdmi_disconnect, + + .enable = omapdss_hdmi_display_enable, + .disable = omapdss_hdmi_display_disable, + + .check_timings = omapdss_hdmi_display_check_timing, + .set_timings = omapdss_hdmi_display_set_timing, + .get_timings = omapdss_hdmi_display_get_timings, + + .read_edid = hdmi_read_edid, + + .audio_enable = omapdss_hdmi_audio_enable, + .audio_disable = omapdss_hdmi_audio_disable, + .audio_start = omapdss_hdmi_audio_start, + .audio_stop = omapdss_hdmi_audio_stop, + .audio_supported = omapdss_hdmi_audio_supported, + .audio_config = omapdss_hdmi_audio_config, +}; + static void hdmi_init_output(struct platform_device *pdev) { struct omap_dss_device *out = &hdmi.output; @@ -1034,6 +1251,7 @@ static void hdmi_init_output(struct platform_device *pdev) out->output_type = OMAP_DISPLAY_TYPE_HDMI; out->name = "hdmi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; + out->ops.hdmi = &hdmi_ops; out->owner = THIS_MODULE; omapdss_register_output(out); @@ -1083,6 +1301,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi.ip_data.pll_offset = HDMI_PLLCTRL; hdmi.ip_data.phy_offset = HDMI_PHY; + hdmi.ct_cp_hpd_gpio = -1; + hdmi.ls_oe_gpio = -1; + hdmi.hpd_gpio = -1; + hdmi_init_output(pdev); r = hdmi_panel_init(); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index adb103633bd1..709e8015f324 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -172,6 +172,11 @@ enum omap_dss_audio_state { OMAP_DSS_AUDIO_PLAYING, }; +struct omap_dss_audio { + struct snd_aes_iec958 *iec; + struct snd_cea_861_aud_if *cea; +}; + enum omap_dss_rotation_type { OMAP_DSS_ROT_DMA = 1 << 0, OMAP_DSS_ROT_VRFB = 1 << 1, @@ -653,6 +658,39 @@ struct omapdss_atv_ops { u32 (*get_wss)(struct omap_dss_device *dssdev); }; +struct omapdss_hdmi_ops { + int (*connect)(struct omap_dss_device *dssdev, + struct omap_dss_device *dst); + void (*disconnect)(struct omap_dss_device *dssdev, + struct omap_dss_device *dst); + + int (*enable)(struct omap_dss_device *dssdev); + void (*disable)(struct omap_dss_device *dssdev); + + int (*check_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + void (*set_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + void (*get_timings)(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); + + int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); + bool (*detect)(struct omap_dss_device *dssdev); + + /* + * Note: These functions might sleep. Do not call while + * holding a spinlock/readlock. + */ + int (*audio_enable)(struct omap_dss_device *dssdev); + void (*audio_disable)(struct omap_dss_device *dssdev); + bool (*audio_supported)(struct omap_dss_device *dssdev); + int (*audio_config)(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio); + /* Note: These functions may not sleep */ + int (*audio_start)(struct omap_dss_device *dssdev); + void (*audio_stop)(struct omap_dss_device *dssdev); +}; + struct omap_dss_device { /* old device, to be removed */ struct device old_dev; @@ -722,6 +760,7 @@ struct omap_dss_device { const struct omapdss_dpi_ops *dpi; const struct omapdss_sdi_ops *sdi; const struct omapdss_dvi_ops *dvi; + const struct omapdss_hdmi_ops *hdmi; const struct omapdss_atv_ops *atv; } ops; @@ -759,11 +798,6 @@ struct omap_dss_hdmi_data int hpd_gpio; }; -struct omap_dss_audio { - struct snd_aes_iec958 *iec; - struct snd_cea_861_aud_if *cea; -}; - struct omap_dss_driver { struct device_driver driver;