]> Git Repo - J-linux.git/blobdiff - drivers/gpu/drm/omapdrm/omap_connector.c
Merge branch 'asoc-5.3' into asoc-5.4
[J-linux.git] / drivers / gpu / drm / omapdrm / omap_connector.c
index 9da94d10782a8829a5cc2376bfe12929d9fda9fa..5b8799c69f684936a96034665c38b2aef84a6408 100644 (file)
@@ -1,22 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
  * Author: Rob Clark <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #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"
 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 +60,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 +96,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 +121,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 +135,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 +144,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 +158,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 +203,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 +216,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 +297,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 +329,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);
 
        /*
This page took 0.040674 seconds and 4 git commands to generate.