diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_connector.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_connector.c | 181 |
1 files changed, 84 insertions, 97 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 9da94d10782a..5967283934e1 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -17,6 +17,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> +#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include "omap_drv.h" @@ -30,24 +31,27 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *output; - struct omap_dss_device *display; struct omap_dss_device *hpd; bool hdmi_mode; }; static void omap_connector_hpd_notify(struct drm_connector *connector, - struct omap_dss_device *src, enum drm_connector_status status) { - if (status == connector_status_disconnected) { - /* - * If the source is an HDMI encoder, notify it of disconnection. - * This is required to let the HDMI encoder reset any internal - * state related to connection status, such as the CEC address. - */ - if (src && src->type == OMAP_DISPLAY_TYPE_HDMI && - src->ops->hdmi.lost_hotplug) - src->ops->hdmi.lost_hotplug(src); + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev; + + if (status != connector_status_disconnected) + return; + + /* + * Notify all devics in the pipeline of disconnection. This is required + * to let the HDMI encoders reset their internal state related to + * connection status, such as the CEC address. + */ + for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) { + if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug) + dssdev->ops->hdmi.lost_hotplug(dssdev); } } @@ -67,7 +71,7 @@ static void omap_connector_hpd_cb(void *cb_data, if (old_status == status) return; - omap_connector_hpd_notify(connector, omap_connector->hpd, status); + omap_connector_hpd_notify(connector, status); drm_kms_helper_hotplug_event(dev); } @@ -103,20 +107,20 @@ omap_connector_find_device(struct drm_connector *connector, enum omap_dss_device_ops_flag op) { struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev; + struct omap_dss_device *dssdev = NULL; + struct omap_dss_device *d; - for (dssdev = omap_connector->display; dssdev; dssdev = dssdev->src) { - if (dssdev->ops_flags & op) - return dssdev; + for (d = omap_connector->output; d; d = d->next) { + if (d->ops_flags & op) + dssdev = d; } - return NULL; + return dssdev; } static enum drm_connector_status omap_connector_detect( struct drm_connector *connector, bool force) { - struct omap_connector *omap_connector = to_omap_connector(connector); struct omap_dss_device *dssdev; enum drm_connector_status status; @@ -128,13 +132,12 @@ static enum drm_connector_status omap_connector_detect( ? connector_status_connected : connector_status_disconnected; - omap_connector_hpd_notify(connector, dssdev->src, status); + omap_connector_hpd_notify(connector, status); } else { - switch (omap_connector->display->type) { - case OMAP_DISPLAY_TYPE_DPI: - case OMAP_DISPLAY_TYPE_DBI: - case OMAP_DISPLAY_TYPE_SDI: - case OMAP_DISPLAY_TYPE_DSI: + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DPI: + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_DSI: status = connector_status_connected; break; default: @@ -143,7 +146,7 @@ static enum drm_connector_status omap_connector_detect( } } - VERB("%s: %d (force=%d)", omap_connector->display->name, status, force); + VERB("%s: %d (force=%d)", connector->name, status, force); return status; } @@ -152,7 +155,7 @@ static void omap_connector_destroy(struct drm_connector *connector) { struct omap_connector *omap_connector = to_omap_connector(connector); - DBG("%s", omap_connector->display->name); + DBG("%s", connector->name); if (omap_connector->hpd) { struct omap_dss_device *hpd = omap_connector->hpd; @@ -166,7 +169,6 @@ static void omap_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); omapdss_device_put(omap_connector->output); - omapdss_device_put(omap_connector->display); kfree(omap_connector); } @@ -212,10 +214,8 @@ static int omap_connector_get_modes(struct drm_connector *connector) { struct omap_connector *omap_connector = to_omap_connector(connector); struct omap_dss_device *dssdev; - struct drm_display_mode *mode; - struct videomode vm = {0}; - DBG("%s", omap_connector->display->name); + DBG("%s", connector->name); /* * If display exposes EDID, then we parse that in the normal way to @@ -227,89 +227,71 @@ static int omap_connector_get_modes(struct drm_connector *connector) return omap_connector_get_modes_edid(connector, dssdev); /* - * Otherwise we have either a fixed resolution panel or an output that - * doesn't support modes discovery (e.g. DVI or VGA with the DDC bus - * unconnected, or analog TV). Start by querying the size. + * Otherwise if the display pipeline reports modes (e.g. with a fixed + * resolution panel or an analog TV output), query it. */ - dssdev = omap_connector->display; - if (dssdev->driver && dssdev->driver->get_size) - dssdev->driver->get_size(dssdev, - &connector->display_info.width_mm, - &connector->display_info.height_mm); + dssdev = omap_connector_find_device(connector, + OMAP_DSS_DEVICE_OP_MODES); + if (dssdev) + return dssdev->ops->get_modes(dssdev, connector); /* - * Iterate over the pipeline to find the first device that can provide - * timing information. If we can't find any, we just let the KMS core - * add the default modes. + * Otherwise if the display pipeline uses a drm_panel, we delegate the + * operation to the panel API. */ - for (dssdev = omap_connector->display; dssdev; dssdev = dssdev->src) { - if (dssdev->ops->get_timings) - break; - } - if (!dssdev) - return 0; + if (omap_connector->output->panel) + return drm_panel_get_modes(omap_connector->output->panel); - /* Add a single mode corresponding to the fixed panel timings. */ - mode = drm_mode_create(connector->dev); - if (!mode) - return 0; + /* + * We can't retrieve modes, which can happen for instance for a DVI or + * VGA output with the DDC bus unconnected. The KMS core will add the + * default modes. + */ + return 0; +} - dssdev->ops->get_timings(dssdev, &vm); +enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int ret; - drm_display_mode_from_videomode(&vm, mode); + drm_mode_copy(adjusted_mode, mode); - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); + for (; dssdev; dssdev = dssdev->next) { + if (!dssdev->ops->check_timings) + continue; + + ret = dssdev->ops->check_timings(dssdev, adjusted_mode); + if (ret) + return MODE_BAD; + } - return 1; + return MODE_OK; } -static int omap_connector_mode_valid(struct drm_connector *connector, +static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct omap_connector *omap_connector = to_omap_connector(connector); - enum omap_channel channel = omap_connector->output->dispc_channel; - struct omap_drm_private *priv = connector->dev->dev_private; - struct omap_dss_device *dssdev; - struct videomode vm = {0}; - struct drm_device *dev = connector->dev; - struct drm_display_mode *new_mode; - int r, ret = MODE_BAD; - - drm_display_mode_to_videomode(mode, &vm); - mode->vrefresh = drm_mode_vrefresh(mode); + struct drm_display_mode new_mode = { { 0 } }; + enum drm_mode_status status; - r = priv->dispc_ops->mgr_check_timings(priv->dispc, channel, &vm); - if (r) + status = omap_connector_mode_fixup(omap_connector->output, mode, + &new_mode); + if (status != MODE_OK) goto done; - for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) { - if (!dssdev->ops->check_timings) - continue; - - r = dssdev->ops->check_timings(dssdev, &vm); - if (r) - goto done; - } - - /* check if vrefresh is still valid */ - new_mode = drm_mode_duplicate(dev, mode); - if (!new_mode) - return MODE_BAD; - - new_mode->clock = vm.pixelclock / 1000; - new_mode->vrefresh = 0; - if (mode->vrefresh == drm_mode_vrefresh(new_mode)) - ret = MODE_OK; - drm_mode_destroy(dev, new_mode); + /* Check if vrefresh is still valid. */ + if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode)) + status = MODE_NOCLOCK; done: DBG("connector: mode %s: " DRM_MODE_FMT, - (ret == MODE_OK) ? "valid" : "invalid", + (status == MODE_OK) ? "valid" : "invalid", DRM_MODE_ARG(mode)); - return ret; + return status; } static const struct drm_connector_funcs omap_connector_funcs = { @@ -326,9 +308,16 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { .mode_valid = omap_connector_mode_valid, }; -static int omap_connector_get_type(struct omap_dss_device *display) +static int omap_connector_get_type(struct omap_dss_device *output) { - switch (display->type) { + struct omap_dss_device *display; + enum omap_display_type type; + + display = omapdss_display_get(output); + type = display->type; + omapdss_device_put(display); + + switch (type) { case OMAP_DISPLAY_TYPE_HDMI: return DRM_MODE_CONNECTOR_HDMIA; case OMAP_DISPLAY_TYPE_DVI: @@ -351,28 +340,26 @@ static int omap_connector_get_type(struct omap_dss_device *display) /* initialize connector */ struct drm_connector *omap_connector_init(struct drm_device *dev, struct omap_dss_device *output, - struct omap_dss_device *display, struct drm_encoder *encoder) { struct drm_connector *connector = NULL; struct omap_connector *omap_connector; struct omap_dss_device *dssdev; - DBG("%s", display->name); + DBG("%s", output->name); omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL); if (!omap_connector) goto fail; omap_connector->output = omapdss_device_get(output); - omap_connector->display = omapdss_device_get(display); connector = &omap_connector->base; connector->interlace_allowed = 1; connector->doublescan_allowed = 0; drm_connector_init(dev, connector, &omap_connector_funcs, - omap_connector_get_type(display)); + omap_connector_get_type(output)); drm_connector_helper_add(connector, &omap_connector_helper_funcs); /* |