diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_encoder.c')
| -rw-r--r-- | drivers/gpu/drm/omapdrm/omap_encoder.c | 211 | 
1 files changed, 129 insertions, 82 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 0d85b3a35767..40512419642b 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -20,6 +20,7 @@  #include <drm/drm_crtc.h>  #include <drm/drm_modeset_helper_vtables.h>  #include <drm/drm_edid.h> +#include <drm/drm_panel.h>  #include "omap_drv.h" @@ -37,7 +38,6 @@  struct omap_encoder {  	struct drm_encoder base;  	struct omap_dss_device *output; -	struct omap_dss_device *display;  };  static void omap_encoder_destroy(struct drm_encoder *encoder) @@ -52,22 +52,43 @@ static const struct drm_encoder_funcs omap_encoder_funcs = {  	.destroy = omap_encoder_destroy,  }; -static void omap_encoder_hdmi_mode_set(struct drm_encoder *encoder, +static void omap_encoder_update_videomode_flags(struct videomode *vm, +						u32 bus_flags) +{ +	if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW | +			   DISPLAY_FLAGS_DE_HIGH))) { +		if (bus_flags & DRM_BUS_FLAG_DE_LOW) +			vm->flags |= DISPLAY_FLAGS_DE_LOW; +		else if (bus_flags & DRM_BUS_FLAG_DE_HIGH) +			vm->flags |= DISPLAY_FLAGS_DE_HIGH; +	} + +	if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | +			   DISPLAY_FLAGS_PIXDATA_NEGEDGE))) { +		if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) +			vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; +		else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) +			vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; +	} + +	if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE | +			   DISPLAY_FLAGS_SYNC_NEGEDGE))) { +		if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE) +			vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE; +		else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE) +			vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; +	} +} + +static void omap_encoder_hdmi_mode_set(struct drm_connector *connector, +				       struct drm_encoder *encoder,  				       struct drm_display_mode *adjusted_mode)  { -	struct drm_device *dev = encoder->dev;  	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);  	struct omap_dss_device *dssdev = omap_encoder->output; -	struct drm_connector *connector;  	bool hdmi_mode; -	hdmi_mode = false; -	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -		if (connector->encoder == encoder) { -			hdmi_mode = omap_connector_get_hdmi_mode(connector); -			break; -		} -	} +	hdmi_mode = omap_connector_get_hdmi_mode(connector);  	if (dssdev->ops->hdmi.set_hdmi_mode)  		dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode); @@ -88,8 +109,18 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,  				  struct drm_display_mode *adjusted_mode)  {  	struct omap_encoder *omap_encoder = to_omap_encoder(encoder); +	struct omap_dss_device *output = omap_encoder->output;  	struct omap_dss_device *dssdev; +	struct drm_device *dev = encoder->dev; +	struct drm_connector *connector; +	struct drm_bridge *bridge;  	struct videomode vm = { 0 }; +	u32 bus_flags; + +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		if (connector->encoder == encoder) +			break; +	}  	drm_display_mode_to_videomode(adjusted_mode, &vm); @@ -102,66 +133,102 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,  	 *  	 * A better solution is to use DRM's bus-flags through the whole driver.  	 */ -	for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) { -		unsigned long bus_flags = dssdev->bus_flags; - -		if (!(vm.flags & (DISPLAY_FLAGS_DE_LOW | -				  DISPLAY_FLAGS_DE_HIGH))) { -			if (bus_flags & DRM_BUS_FLAG_DE_LOW) -				vm.flags |= DISPLAY_FLAGS_DE_LOW; -			else if (bus_flags & DRM_BUS_FLAG_DE_HIGH) -				vm.flags |= DISPLAY_FLAGS_DE_HIGH; -		} - -		if (!(vm.flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | -				  DISPLAY_FLAGS_PIXDATA_NEGEDGE))) { -			if (bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) -				vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; -			else if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) -				vm.flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; -		} - -		if (!(vm.flags & (DISPLAY_FLAGS_SYNC_POSEDGE | -				  DISPLAY_FLAGS_SYNC_NEGEDGE))) { -			if (bus_flags & DRM_BUS_FLAG_SYNC_POSEDGE) -				vm.flags |= DISPLAY_FLAGS_SYNC_POSEDGE; -			else if (bus_flags & DRM_BUS_FLAG_SYNC_NEGEDGE) -				vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; -		} +	for (dssdev = output; dssdev; dssdev = dssdev->next) +		omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags); + +	for (bridge = output->bridge; bridge; bridge = bridge->next) { +		if (!bridge->timings) +			continue; + +		bus_flags = bridge->timings->input_bus_flags; +		omap_encoder_update_videomode_flags(&vm, bus_flags);  	} +	bus_flags = connector->display_info.bus_flags; +	omap_encoder_update_videomode_flags(&vm, bus_flags); +  	/* Set timings for all devices in the display pipeline. */ -	dss_mgr_set_timings(omap_encoder->output, &vm); +	dss_mgr_set_timings(output, &vm); -	for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) { +	for (dssdev = output; dssdev; dssdev = dssdev->next) {  		if (dssdev->ops->set_timings) -			dssdev->ops->set_timings(dssdev, &vm); +			dssdev->ops->set_timings(dssdev, adjusted_mode);  	}  	/* Set the HDMI mode and HDMI infoframe if applicable. */ -	if (omap_encoder->output->output_type == OMAP_DISPLAY_TYPE_HDMI) -		omap_encoder_hdmi_mode_set(encoder, adjusted_mode); +	if (output->type == OMAP_DISPLAY_TYPE_HDMI) +		omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);  }  static void omap_encoder_disable(struct drm_encoder *encoder)  {  	struct omap_encoder *omap_encoder = to_omap_encoder(encoder); -	struct omap_dss_device *dssdev = omap_encoder->display; +	struct omap_dss_device *dssdev = omap_encoder->output; +	struct drm_device *dev = encoder->dev; + +	dev_dbg(dev->dev, "disable(%s)\n", dssdev->name); + +	/* Disable the panel if present. */ +	if (dssdev->panel) { +		drm_panel_disable(dssdev->panel); +		drm_panel_unprepare(dssdev->panel); +	} + +	/* +	 * Disable the chain of external devices, starting at the one at the +	 * internal encoder's output. +	 */ +	omapdss_device_disable(dssdev->next); + +	/* +	 * Disable the internal encoder. This will disable the DSS output. The +	 * DSI is treated as an exception as DSI pipelines still use the legacy +	 * flow where the pipeline output controls the encoder. +	 */ +	if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) { +		dssdev->ops->disable(dssdev); +		dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +	} -	dssdev->ops->disable(dssdev); +	/* +	 * Perform the post-disable operations on the chain of external devices +	 * to complete the display pipeline disable. +	 */ +	omapdss_device_post_disable(dssdev->next);  }  static void omap_encoder_enable(struct drm_encoder *encoder)  {  	struct omap_encoder *omap_encoder = to_omap_encoder(encoder); -	struct omap_dss_device *dssdev = omap_encoder->display; -	int r; - -	r = dssdev->ops->enable(dssdev); -	if (r) -		dev_err(encoder->dev->dev, -			"Failed to enable display '%s': %d\n", -			dssdev->name, r); +	struct omap_dss_device *dssdev = omap_encoder->output; +	struct drm_device *dev = encoder->dev; + +	dev_dbg(dev->dev, "enable(%s)\n", dssdev->name); + +	/* Prepare the chain of external devices for pipeline enable. */ +	omapdss_device_pre_enable(dssdev->next); + +	/* +	 * Enable the internal encoder. This will enable the DSS output. The +	 * DSI is treated as an exception as DSI pipelines still use the legacy +	 * flow where the pipeline output controls the encoder. +	 */ +	if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) { +		dssdev->ops->enable(dssdev); +		dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +	} + +	/* +	 * Enable the chain of external devices, starting at the one at the +	 * internal encoder's output. +	 */ +	omapdss_device_enable(dssdev->next); + +	/* Enable the panel if present. */ +	if (dssdev->panel) { +		drm_panel_prepare(dssdev->panel); +		drm_panel_enable(dssdev->panel); +	}  }  static int omap_encoder_atomic_check(struct drm_encoder *encoder, @@ -169,35 +236,17 @@ static int omap_encoder_atomic_check(struct drm_encoder *encoder,  				     struct drm_connector_state *conn_state)  {  	struct omap_encoder *omap_encoder = to_omap_encoder(encoder); -	enum omap_channel channel = omap_encoder->output->dispc_channel; -	struct drm_device *dev = encoder->dev; -	struct omap_drm_private *priv = dev->dev_private; -	struct omap_dss_device *dssdev; -	struct videomode vm = { 0 }; -	int ret; - -	drm_display_mode_to_videomode(&crtc_state->mode, &vm); - -	ret = priv->dispc_ops->mgr_check_timings(priv->dispc, channel, &vm); -	if (ret) -		goto done; - -	for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) { -		if (!dssdev->ops->check_timings) -			continue; - -		ret = dssdev->ops->check_timings(dssdev, &vm); -		if (ret) -			goto done; +	enum drm_mode_status status; + +	status = omap_connector_mode_fixup(omap_encoder->output, +					   &crtc_state->mode, +					   &crtc_state->adjusted_mode); +	if (status != MODE_OK) { +		dev_err(encoder->dev->dev, "invalid timings: %d\n", status); +		return -EINVAL;  	} -	drm_display_mode_from_videomode(&vm, &crtc_state->adjusted_mode); - -done: -	if (ret) -		dev_err(dev->dev, "invalid timings: %d\n", ret); - -	return ret; +	return 0;  }  static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { @@ -209,8 +258,7 @@ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {  /* initialize encoder */  struct drm_encoder *omap_encoder_init(struct drm_device *dev, -				      struct omap_dss_device *output, -				      struct omap_dss_device *display) +				      struct omap_dss_device *output)  {  	struct drm_encoder *encoder = NULL;  	struct omap_encoder *omap_encoder; @@ -220,7 +268,6 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev,  		goto fail;  	omap_encoder->output = output; -	omap_encoder->display = display;  	encoder = &omap_encoder->base;  |