diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/dss/hdmi4.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi4.c | 313 |
1 files changed, 176 insertions, 137 deletions
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 0f557fad4513..2578c95570f6 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -28,6 +28,9 @@ #include <sound/omap-hdmi-audio.h> #include <media/cec.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_state_helper.h> + #include "omapdss.h" #include "hdmi4_core.h" #include "hdmi4_cec.h" @@ -237,20 +240,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi) hdmi_power_off_core(hdmi); } -static void hdmi_display_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - mutex_lock(&hdmi->lock); - - drm_display_mode_to_videomode(mode, &hdmi->cfg.vm); - - dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000); - - mutex_unlock(&hdmi->lock); -} - static int hdmi_dump_regs(struct seq_file *s, void *p) { struct omap_hdmi *hdmi = s->private; @@ -272,57 +261,139 @@ static int hdmi_dump_regs(struct seq_file *s, void *p) return 0; } -static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len) +static void hdmi_start_audio_stream(struct omap_hdmi *hd) { - int r; + hdmi_wp_audio_enable(&hd->wp, true); + hdmi4_audio_start(&hd->core, &hd->wp); +} - mutex_lock(&hdmi->lock); +static void hdmi_stop_audio_stream(struct omap_hdmi *hd) +{ + hdmi4_audio_stop(&hd->core, &hd->wp); + hdmi_wp_audio_enable(&hd->wp, false); +} - r = hdmi_runtime_get(hdmi); - BUG_ON(r); +int hdmi4_core_enable(struct hdmi_core_data *core) +{ + struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); + int r = 0; - r = hdmi4_read_edid(&hdmi->core, buf, len); + DSSDBG("ENTER omapdss_hdmi4_core_enable\n"); + + mutex_lock(&hdmi->lock); + + r = hdmi_power_on_core(hdmi); + if (r) { + DSSERR("failed to power on device\n"); + goto err0; + } - hdmi_runtime_put(hdmi); mutex_unlock(&hdmi->lock); + return 0; +err0: + mutex_unlock(&hdmi->lock); return r; } -static void hdmi_start_audio_stream(struct omap_hdmi *hd) +void hdmi4_core_disable(struct hdmi_core_data *core) { - hdmi_wp_audio_enable(&hd->wp, true); - hdmi4_audio_start(&hd->core, &hd->wp); + struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); + + DSSDBG("Enter omapdss_hdmi4_core_disable\n"); + + mutex_lock(&hdmi->lock); + + hdmi_power_off_core(hdmi); + + mutex_unlock(&hdmi->lock); } -static void hdmi_stop_audio_stream(struct omap_hdmi *hd) +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int hdmi4_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { - hdmi4_audio_stop(&hd->core, &hd->wp); - hdmi_wp_audio_enable(&hd->wp, false); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge, + bridge, flags); } -static void hdmi_display_enable(struct omap_dss_device *dssdev) +static void hdmi4_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + mutex_lock(&hdmi->lock); + + drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm); + + dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000); + + mutex_unlock(&hdmi->lock); +} + +static void hdmi4_bridge_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct drm_atomic_state *state = bridge_state->base.state; + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_crtc_state *crtc_state; unsigned long flags; - int r; + int ret; + + /* + * None of these should fail, as the bridge can't be enabled without a + * valid CRTC to connector path with fully populated new states. + */ + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi + ? HDMI_HDMI : HDMI_DVI; - DSSDBG("ENTER hdmi_display_enable\n"); + if (connector->display_info.is_hdmi) { + const struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi; + + mode = &crtc_state->adjusted_mode; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, + mode); + if (ret == 0) + hdmi->cfg.infoframe = avi; + } mutex_lock(&hdmi->lock); - r = hdmi_power_on_full(hdmi); - if (r) { + ret = hdmi_power_on_full(hdmi); + if (ret) { DSSERR("failed to power on device\n"); goto done; } if (hdmi->audio_configured) { - r = hdmi4_audio_config(&hdmi->core, &hdmi->wp, - &hdmi->audio_config, - hdmi->cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); + ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp, + &hdmi->audio_config, + hdmi->cfg.vm.pixelclock); + if (ret) { + DSSERR("Error restoring audio configuration: %d", ret); hdmi->audio_abort_cb(&hdmi->pdev->dev); hdmi->audio_configured = false; } @@ -338,13 +409,12 @@ done: mutex_unlock(&hdmi->lock); } -static void hdmi_display_disable(struct omap_dss_device *dssdev) +static void hdmi4_bridge_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); unsigned long flags; - DSSDBG("Enter hdmi_display_disable\n"); - mutex_lock(&hdmi->lock); spin_lock_irqsave(&hdmi->audio_playing_lock, flags); @@ -357,58 +427,21 @@ static void hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi->lock); } -int hdmi4_core_enable(struct hdmi_core_data *core) +static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) { - struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); - int r = 0; - - DSSDBG("ENTER omapdss_hdmi4_core_enable\n"); - - mutex_lock(&hdmi->lock); - - r = hdmi_power_on_core(hdmi); - if (r) { - DSSERR("failed to power on device\n"); - goto err0; - } - - mutex_unlock(&hdmi->lock); - return 0; - -err0: - mutex_unlock(&hdmi->lock); - return r; -} - -void hdmi4_core_disable(struct hdmi_core_data *core) -{ - struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); - - DSSDBG("Enter omapdss_hdmi4_core_disable\n"); - - mutex_lock(&hdmi->lock); - - hdmi_power_off_core(hdmi); - - mutex_unlock(&hdmi->lock); -} - -static int hdmi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return omapdss_device_connect(dst->dss, dst, dst->next); -} + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); -static void hdmi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); + if (status == connector_status_disconnected) + hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); } -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) +static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct edid *edid = NULL; + unsigned int cec_addr; bool need_enable; int r; @@ -417,63 +450,65 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, if (need_enable) { r = hdmi4_core_enable(&hdmi->core); if (r) - return r; + return NULL; } - r = read_edid(hdmi, edid, len); - if (r >= 256) - hdmi4_cec_set_phys_addr(&hdmi->core, - cec_get_edid_phys_addr(edid, r, NULL)); - else - hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); - if (need_enable) - hdmi4_core_disable(&hdmi->core); + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); - return r; -} + r = hdmi4_core_ddc_init(&hdmi->core); + if (r) + goto done; -static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core); - hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); -} +done: + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + if (edid && edid->extensions) { + unsigned int len = (edid->extensions + 1) * EDID_LENGTH; - hdmi->cfg.infoframe = *avi; - return 0; -} + cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL); + } else { + cec_addr = CEC_PHYS_ADDR_INVALID; + } -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); - hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; -} + if (need_enable) + hdmi4_core_disable(&hdmi->core); -static const struct omap_dss_device_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, + return edid; +} - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, +static const struct drm_bridge_funcs hdmi4_bridge_funcs = { + .attach = hdmi4_bridge_attach, + .mode_set = hdmi4_bridge_mode_set, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = hdmi4_bridge_enable, + .atomic_disable = hdmi4_bridge_disable, + .hpd_notify = hdmi4_bridge_hpd_notify, + .get_edid = hdmi4_bridge_get_edid, +}; - .set_timings = hdmi_display_set_timings, +static void hdmi4_bridge_init(struct omap_hdmi *hdmi) +{ + hdmi->bridge.funcs = &hdmi4_bridge_funcs; + hdmi->bridge.of_node = hdmi->pdev->dev.of_node; + hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; - .read_edid = hdmi_read_edid, + drm_bridge_add(&hdmi->bridge); +} - .hdmi = { - .lost_hotplug = hdmi_lost_hotplug, - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, - }, -}; +static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi) +{ + drm_bridge_remove(&hdmi->bridge); +} /* ----------------------------------------------------------------------------- * Audio Callbacks @@ -666,19 +701,21 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) struct omap_dss_device *out = &hdmi->output; int r; + hdmi4_bridge_init(hdmi); + out->dev = &hdmi->pdev->dev; out->id = OMAP_DSS_OUTPUT_HDMI; out->type = OMAP_DISPLAY_TYPE_HDMI; out->name = "hdmi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops = &hdmi_ops; out->owner = THIS_MODULE; - out->of_ports = BIT(0); - out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; + out->of_port = 0; - r = omapdss_device_init_output(out); - if (r < 0) + r = omapdss_device_init_output(out, &hdmi->bridge); + if (r < 0) { + hdmi4_bridge_cleanup(hdmi); return r; + } omapdss_device_register(out); @@ -691,6 +728,8 @@ static void hdmi4_uninit_output(struct omap_hdmi *hdmi) omapdss_device_unregister(out); omapdss_device_cleanup_output(out); + + hdmi4_bridge_cleanup(hdmi); } static int hdmi4_probe_of(struct omap_hdmi *hdmi) |