diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/dss/hdmi5.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/dss/hdmi5.c | 295 |
1 files changed, 162 insertions, 133 deletions
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index d9463b332554..4d4c1fabd0a1 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -31,6 +31,9 @@ #include <linux/of_graph.h> #include <sound/omap-hdmi-audio.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_state_helper.h> + #include "omapdss.h" #include "hdmi5_core.h" #include "dss.h" @@ -236,20 +239,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; @@ -271,66 +260,138 @@ 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; - int idlemode; + REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + hdmi_wp_audio_enable(&hd->wp, true); + hdmi_wp_audio_core_req_enable(&hd->wp, true); +} - mutex_lock(&hdmi->lock); +static void hdmi_stop_audio_stream(struct omap_hdmi *hd) +{ + hdmi_wp_audio_core_req_enable(&hd->wp, false); + hdmi_wp_audio_enable(&hd->wp, false); + REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); +} - r = hdmi_runtime_get(hdmi); - BUG_ON(r); +static int hdmi_core_enable(struct omap_hdmi *hdmi) +{ + int r = 0; - idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); - /* No-idle mode */ - REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + DSSDBG("ENTER omapdss_hdmi_core_enable\n"); - r = hdmi5_read_edid(&hdmi->core, buf, len); + mutex_lock(&hdmi->lock); - REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); + 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) +static void hdmi_core_disable(struct omap_hdmi *hdmi) { - REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); - hdmi_wp_audio_enable(&hd->wp, true); - hdmi_wp_audio_core_req_enable(&hd->wp, true); + DSSDBG("Enter omapdss_hdmi_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 hdmi5_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { - hdmi_wp_audio_core_req_enable(&hd->wp, false); - hdmi_wp_audio_enable(&hd->wp, false); - REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); + 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 hdmi5_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); - unsigned long flags; - int r; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + mutex_lock(&hdmi->lock); - DSSDBG("ENTER hdmi_display_enable\n"); + 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 hdmi5_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 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; + + 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 = hdmi5_audio_config(&hdmi->core, &hdmi->wp, - &hdmi->audio_config, - hdmi->cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); + ret = hdmi5_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; } @@ -346,13 +407,12 @@ done: mutex_unlock(&hdmi->lock); } -static void hdmi_display_disable(struct omap_dss_device *dssdev) +static void hdmi5_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); @@ -365,109 +425,74 @@ static void hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi->lock); } -static int hdmi_core_enable(struct omap_hdmi *hdmi) +static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) { - int r = 0; - - DSSDBG("ENTER omapdss_hdmi_core_enable\n"); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct edid *edid; + bool need_enable; + int idlemode; + int r; - mutex_lock(&hdmi->lock); + need_enable = hdmi->core_enabled == false; - r = hdmi_power_on_core(hdmi); - if (r) { - DSSERR("failed to power on device\n"); - goto err0; + if (need_enable) { + r = hdmi_core_enable(hdmi); + if (r) + return NULL; } - mutex_unlock(&hdmi->lock); - return 0; - -err0: - mutex_unlock(&hdmi->lock); - return r; -} - -static void hdmi_core_disable(struct omap_hdmi *hdmi) -{ - DSSDBG("Enter omapdss_hdmi_core_disable\n"); - mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); - 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); -} + idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); + /* No-idle mode */ + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); -static void hdmi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} + hdmi5_core_ddc_init(&hdmi->core); -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - bool need_enable; - int r; + edid = drm_do_get_edid(connector, hdmi5_core_ddc_read, &hdmi->core); - need_enable = hdmi->core_enabled == false; + hdmi5_core_ddc_uninit(&hdmi->core); - if (need_enable) { - r = hdmi_core_enable(hdmi); - if (r) - return r; - } + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); - r = read_edid(hdmi, edid, len); + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); if (need_enable) hdmi_core_disable(hdmi); - return r; + return (struct edid *)edid; } -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) +static const struct drm_bridge_funcs hdmi5_bridge_funcs = { + .attach = hdmi5_bridge_attach, + .mode_set = hdmi5_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 = hdmi5_bridge_enable, + .atomic_disable = hdmi5_bridge_disable, + .get_edid = hdmi5_bridge_get_edid, +}; + +static void hdmi5_bridge_init(struct omap_hdmi *hdmi) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + hdmi->bridge.funcs = &hdmi5_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; - hdmi->cfg.infoframe = *avi; - return 0; + drm_bridge_add(&hdmi->bridge); } -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) +static void hdmi5_bridge_cleanup(struct omap_hdmi *hdmi) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; + drm_bridge_remove(&hdmi->bridge); } -static const struct omap_dss_device_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, - - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, - - .set_timings = hdmi_display_set_timings, - - .read_edid = hdmi_read_edid, - - .hdmi = { - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, - }, -}; - /* ----------------------------------------------------------------------------- * Audio Callbacks */ @@ -650,19 +675,21 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) struct omap_dss_device *out = &hdmi->output; int r; + hdmi5_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) { + hdmi5_bridge_cleanup(hdmi); return r; + } omapdss_device_register(out); @@ -675,6 +702,8 @@ static void hdmi5_uninit_output(struct omap_hdmi *hdmi) omapdss_device_unregister(out); omapdss_device_cleanup_output(out); + + hdmi5_bridge_cleanup(hdmi); } static int hdmi5_probe_of(struct omap_hdmi *hdmi) |