root/drivers/gpu/drm/omapdrm/omap_connector.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap_connector_hpd_notify
  2. omap_connector_hpd_cb
  3. omap_connector_enable_hpd
  4. omap_connector_disable_hpd
  5. omap_connector_get_hdmi_mode
  6. omap_connector_find_device
  7. omap_connector_detect
  8. omap_connector_destroy
  9. omap_connector_get_modes_edid
  10. omap_connector_get_modes
  11. omap_connector_mode_fixup
  12. omap_connector_mode_valid
  13. omap_connector_get_type
  14. omap_connector_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
   4  * Author: Rob Clark <rob@ti.com>
   5  */
   6 
   7 #include <drm/drm_atomic_helper.h>
   8 #include <drm/drm_crtc.h>
   9 #include <drm/drm_panel.h>
  10 #include <drm/drm_probe_helper.h>
  11 
  12 #include "omap_drv.h"
  13 
  14 /*
  15  * connector funcs
  16  */
  17 
  18 #define to_omap_connector(x) container_of(x, struct omap_connector, base)
  19 
  20 struct omap_connector {
  21         struct drm_connector base;
  22         struct omap_dss_device *output;
  23         struct omap_dss_device *hpd;
  24         bool hdmi_mode;
  25 };
  26 
  27 static void omap_connector_hpd_notify(struct drm_connector *connector,
  28                                       enum drm_connector_status status)
  29 {
  30         struct omap_connector *omap_connector = to_omap_connector(connector);
  31         struct omap_dss_device *dssdev;
  32 
  33         if (status != connector_status_disconnected)
  34                 return;
  35 
  36         /*
  37          * Notify all devics in the pipeline of disconnection. This is required
  38          * to let the HDMI encoders reset their internal state related to
  39          * connection status, such as the CEC address.
  40          */
  41         for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
  42                 if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug)
  43                         dssdev->ops->hdmi.lost_hotplug(dssdev);
  44         }
  45 }
  46 
  47 static void omap_connector_hpd_cb(void *cb_data,
  48                                   enum drm_connector_status status)
  49 {
  50         struct omap_connector *omap_connector = cb_data;
  51         struct drm_connector *connector = &omap_connector->base;
  52         struct drm_device *dev = connector->dev;
  53         enum drm_connector_status old_status;
  54 
  55         mutex_lock(&dev->mode_config.mutex);
  56         old_status = connector->status;
  57         connector->status = status;
  58         mutex_unlock(&dev->mode_config.mutex);
  59 
  60         if (old_status == status)
  61                 return;
  62 
  63         omap_connector_hpd_notify(connector, status);
  64 
  65         drm_kms_helper_hotplug_event(dev);
  66 }
  67 
  68 void omap_connector_enable_hpd(struct drm_connector *connector)
  69 {
  70         struct omap_connector *omap_connector = to_omap_connector(connector);
  71         struct omap_dss_device *hpd = omap_connector->hpd;
  72 
  73         if (hpd)
  74                 hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb,
  75                                           omap_connector);
  76 }
  77 
  78 void omap_connector_disable_hpd(struct drm_connector *connector)
  79 {
  80         struct omap_connector *omap_connector = to_omap_connector(connector);
  81         struct omap_dss_device *hpd = omap_connector->hpd;
  82 
  83         if (hpd)
  84                 hpd->ops->unregister_hpd_cb(hpd);
  85 }
  86 
  87 bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
  88 {
  89         struct omap_connector *omap_connector = to_omap_connector(connector);
  90 
  91         return omap_connector->hdmi_mode;
  92 }
  93 
  94 static struct omap_dss_device *
  95 omap_connector_find_device(struct drm_connector *connector,
  96                            enum omap_dss_device_ops_flag op)
  97 {
  98         struct omap_connector *omap_connector = to_omap_connector(connector);
  99         struct omap_dss_device *dssdev = NULL;
 100         struct omap_dss_device *d;
 101 
 102         for (d = omap_connector->output; d; d = d->next) {
 103                 if (d->ops_flags & op)
 104                         dssdev = d;
 105         }
 106 
 107         return dssdev;
 108 }
 109 
 110 static enum drm_connector_status omap_connector_detect(
 111                 struct drm_connector *connector, bool force)
 112 {
 113         struct omap_dss_device *dssdev;
 114         enum drm_connector_status status;
 115 
 116         dssdev = omap_connector_find_device(connector,
 117                                             OMAP_DSS_DEVICE_OP_DETECT);
 118 
 119         if (dssdev) {
 120                 status = dssdev->ops->detect(dssdev)
 121                        ? connector_status_connected
 122                        : connector_status_disconnected;
 123 
 124                 omap_connector_hpd_notify(connector, status);
 125         } else {
 126                 switch (connector->connector_type) {
 127                 case DRM_MODE_CONNECTOR_DPI:
 128                 case DRM_MODE_CONNECTOR_LVDS:
 129                 case DRM_MODE_CONNECTOR_DSI:
 130                         status = connector_status_connected;
 131                         break;
 132                 default:
 133                         status = connector_status_unknown;
 134                         break;
 135                 }
 136         }
 137 
 138         VERB("%s: %d (force=%d)", connector->name, status, force);
 139 
 140         return status;
 141 }
 142 
 143 static void omap_connector_destroy(struct drm_connector *connector)
 144 {
 145         struct omap_connector *omap_connector = to_omap_connector(connector);
 146 
 147         DBG("%s", connector->name);
 148 
 149         if (omap_connector->hpd) {
 150                 struct omap_dss_device *hpd = omap_connector->hpd;
 151 
 152                 hpd->ops->unregister_hpd_cb(hpd);
 153                 omapdss_device_put(hpd);
 154                 omap_connector->hpd = NULL;
 155         }
 156 
 157         drm_connector_unregister(connector);
 158         drm_connector_cleanup(connector);
 159 
 160         omapdss_device_put(omap_connector->output);
 161 
 162         kfree(omap_connector);
 163 }
 164 
 165 #define MAX_EDID  512
 166 
 167 static int omap_connector_get_modes_edid(struct drm_connector *connector,
 168                                          struct omap_dss_device *dssdev)
 169 {
 170         struct omap_connector *omap_connector = to_omap_connector(connector);
 171         enum drm_connector_status status;
 172         void *edid;
 173         int n;
 174 
 175         status = omap_connector_detect(connector, false);
 176         if (status != connector_status_connected)
 177                 goto no_edid;
 178 
 179         edid = kzalloc(MAX_EDID, GFP_KERNEL);
 180         if (!edid)
 181                 goto no_edid;
 182 
 183         if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 ||
 184             !drm_edid_is_valid(edid)) {
 185                 kfree(edid);
 186                 goto no_edid;
 187         }
 188 
 189         drm_connector_update_edid_property(connector, edid);
 190         n = drm_add_edid_modes(connector, edid);
 191 
 192         omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid);
 193 
 194         kfree(edid);
 195         return n;
 196 
 197 no_edid:
 198         drm_connector_update_edid_property(connector, NULL);
 199         return 0;
 200 }
 201 
 202 static int omap_connector_get_modes(struct drm_connector *connector)
 203 {
 204         struct omap_connector *omap_connector = to_omap_connector(connector);
 205         struct omap_dss_device *dssdev;
 206 
 207         DBG("%s", connector->name);
 208 
 209         /*
 210          * If display exposes EDID, then we parse that in the normal way to
 211          * build table of supported modes.
 212          */
 213         dssdev = omap_connector_find_device(connector,
 214                                             OMAP_DSS_DEVICE_OP_EDID);
 215         if (dssdev)
 216                 return omap_connector_get_modes_edid(connector, dssdev);
 217 
 218         /*
 219          * Otherwise if the display pipeline reports modes (e.g. with a fixed
 220          * resolution panel or an analog TV output), query it.
 221          */
 222         dssdev = omap_connector_find_device(connector,
 223                                             OMAP_DSS_DEVICE_OP_MODES);
 224         if (dssdev)
 225                 return dssdev->ops->get_modes(dssdev, connector);
 226 
 227         /*
 228          * Otherwise if the display pipeline uses a drm_panel, we delegate the
 229          * operation to the panel API.
 230          */
 231         if (omap_connector->output->panel)
 232                 return drm_panel_get_modes(omap_connector->output->panel);
 233 
 234         /*
 235          * We can't retrieve modes, which can happen for instance for a DVI or
 236          * VGA output with the DDC bus unconnected. The KMS core will add the
 237          * default modes.
 238          */
 239         return 0;
 240 }
 241 
 242 enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
 243                                         const struct drm_display_mode *mode,
 244                                         struct drm_display_mode *adjusted_mode)
 245 {
 246         int ret;
 247 
 248         drm_mode_copy(adjusted_mode, mode);
 249 
 250         for (; dssdev; dssdev = dssdev->next) {
 251                 if (!dssdev->ops->check_timings)
 252                         continue;
 253 
 254                 ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
 255                 if (ret)
 256                         return MODE_BAD;
 257         }
 258 
 259         return MODE_OK;
 260 }
 261 
 262 static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector,
 263                                  struct drm_display_mode *mode)
 264 {
 265         struct omap_connector *omap_connector = to_omap_connector(connector);
 266         struct drm_display_mode new_mode = { { 0 } };
 267         enum drm_mode_status status;
 268 
 269         status = omap_connector_mode_fixup(omap_connector->output, mode,
 270                                            &new_mode);
 271         if (status != MODE_OK)
 272                 goto done;
 273 
 274         /* Check if vrefresh is still valid. */
 275         if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode))
 276                 status = MODE_NOCLOCK;
 277 
 278 done:
 279         DBG("connector: mode %s: " DRM_MODE_FMT,
 280                         (status == MODE_OK) ? "valid" : "invalid",
 281                         DRM_MODE_ARG(mode));
 282 
 283         return status;
 284 }
 285 
 286 static const struct drm_connector_funcs omap_connector_funcs = {
 287         .reset = drm_atomic_helper_connector_reset,
 288         .detect = omap_connector_detect,
 289         .fill_modes = drm_helper_probe_single_connector_modes,
 290         .destroy = omap_connector_destroy,
 291         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 292         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 293 };
 294 
 295 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
 296         .get_modes = omap_connector_get_modes,
 297         .mode_valid = omap_connector_mode_valid,
 298 };
 299 
 300 static int omap_connector_get_type(struct omap_dss_device *output)
 301 {
 302         struct omap_dss_device *display;
 303         enum omap_display_type type;
 304 
 305         display = omapdss_display_get(output);
 306         type = display->type;
 307         omapdss_device_put(display);
 308 
 309         switch (type) {
 310         case OMAP_DISPLAY_TYPE_HDMI:
 311                 return DRM_MODE_CONNECTOR_HDMIA;
 312         case OMAP_DISPLAY_TYPE_DVI:
 313                 return DRM_MODE_CONNECTOR_DVID;
 314         case OMAP_DISPLAY_TYPE_DSI:
 315                 return DRM_MODE_CONNECTOR_DSI;
 316         case OMAP_DISPLAY_TYPE_DPI:
 317         case OMAP_DISPLAY_TYPE_DBI:
 318                 return DRM_MODE_CONNECTOR_DPI;
 319         case OMAP_DISPLAY_TYPE_VENC:
 320                 /* TODO: This could also be composite */
 321                 return DRM_MODE_CONNECTOR_SVIDEO;
 322         case OMAP_DISPLAY_TYPE_SDI:
 323                 return DRM_MODE_CONNECTOR_LVDS;
 324         default:
 325                 return DRM_MODE_CONNECTOR_Unknown;
 326         }
 327 }
 328 
 329 /* initialize connector */
 330 struct drm_connector *omap_connector_init(struct drm_device *dev,
 331                                           struct omap_dss_device *output,
 332                                           struct drm_encoder *encoder)
 333 {
 334         struct drm_connector *connector = NULL;
 335         struct omap_connector *omap_connector;
 336         struct omap_dss_device *dssdev;
 337 
 338         DBG("%s", output->name);
 339 
 340         omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
 341         if (!omap_connector)
 342                 goto fail;
 343 
 344         omap_connector->output = omapdss_device_get(output);
 345 
 346         connector = &omap_connector->base;
 347         connector->interlace_allowed = 1;
 348         connector->doublescan_allowed = 0;
 349 
 350         drm_connector_init(dev, connector, &omap_connector_funcs,
 351                            omap_connector_get_type(output));
 352         drm_connector_helper_add(connector, &omap_connector_helper_funcs);
 353 
 354         /*
 355          * Initialize connector status handling. First try to find a device that
 356          * supports hot-plug reporting. If it fails, fall back to a device that
 357          * support polling. If that fails too, we don't support hot-plug
 358          * detection at all.
 359          */
 360         dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
 361         if (dssdev) {
 362                 omap_connector->hpd = omapdss_device_get(dssdev);
 363                 connector->polled = DRM_CONNECTOR_POLL_HPD;
 364         } else {
 365                 dssdev = omap_connector_find_device(connector,
 366                                                     OMAP_DSS_DEVICE_OP_DETECT);
 367                 if (dssdev)
 368                         connector->polled = DRM_CONNECTOR_POLL_CONNECT |
 369                                             DRM_CONNECTOR_POLL_DISCONNECT;
 370         }
 371 
 372         return connector;
 373 
 374 fail:
 375         if (connector)
 376                 omap_connector_destroy(connector);
 377 
 378         return NULL;
 379 }

/* [<][>][^][v][top][bottom][index][help] */