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) |