linux/drivers/gpu/drm/omapdrm/omap_connector.c
<<
>>
Prefs
   1/*
   2 * drivers/gpu/drm/omapdrm/omap_connector.c
   3 *
   4 * Copyright (C) 2011 Texas Instruments
   5 * Author: Rob Clark <rob@ti.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <drm/drm_atomic_helper.h>
  21#include <drm/drm_crtc.h>
  22#include <drm/drm_crtc_helper.h>
  23
  24#include "omap_drv.h"
  25
  26/*
  27 * connector funcs
  28 */
  29
  30#define to_omap_connector(x) container_of(x, struct omap_connector, base)
  31
  32struct omap_connector {
  33        struct drm_connector base;
  34        struct omap_dss_device *dssdev;
  35        bool hdmi_mode;
  36};
  37
  38bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
  39{
  40        struct omap_connector *omap_connector = to_omap_connector(connector);
  41
  42        return omap_connector->hdmi_mode;
  43}
  44
  45static enum drm_connector_status omap_connector_detect(
  46                struct drm_connector *connector, bool force)
  47{
  48        struct omap_connector *omap_connector = to_omap_connector(connector);
  49        struct omap_dss_device *dssdev = omap_connector->dssdev;
  50        struct omap_dss_driver *dssdrv = dssdev->driver;
  51        enum drm_connector_status ret;
  52
  53        if (dssdrv->detect) {
  54                if (dssdrv->detect(dssdev))
  55                        ret = connector_status_connected;
  56                else
  57                        ret = connector_status_disconnected;
  58        } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI ||
  59                        dssdev->type == OMAP_DISPLAY_TYPE_DBI ||
  60                        dssdev->type == OMAP_DISPLAY_TYPE_SDI ||
  61                        dssdev->type == OMAP_DISPLAY_TYPE_DSI) {
  62                ret = connector_status_connected;
  63        } else {
  64                ret = connector_status_unknown;
  65        }
  66
  67        VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
  68
  69        return ret;
  70}
  71
  72static void omap_connector_destroy(struct drm_connector *connector)
  73{
  74        struct omap_connector *omap_connector = to_omap_connector(connector);
  75        struct omap_dss_device *dssdev = omap_connector->dssdev;
  76
  77        DBG("%s", omap_connector->dssdev->name);
  78        drm_connector_unregister(connector);
  79        drm_connector_cleanup(connector);
  80        kfree(omap_connector);
  81
  82        omap_dss_put_device(dssdev);
  83}
  84
  85#define MAX_EDID  512
  86
  87static int omap_connector_get_modes(struct drm_connector *connector)
  88{
  89        struct omap_connector *omap_connector = to_omap_connector(connector);
  90        struct omap_dss_device *dssdev = omap_connector->dssdev;
  91        struct omap_dss_driver *dssdrv = dssdev->driver;
  92        struct drm_device *dev = connector->dev;
  93        int n = 0;
  94
  95        DBG("%s", omap_connector->dssdev->name);
  96
  97        /* if display exposes EDID, then we parse that in the normal way to
  98         * build table of supported modes.. otherwise (ie. fixed resolution
  99         * LCD panels) we just return a single mode corresponding to the
 100         * currently configured timings:
 101         */
 102        if (dssdrv->read_edid) {
 103                void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
 104
 105                if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) &&
 106                                drm_edid_is_valid(edid)) {
 107                        drm_mode_connector_update_edid_property(
 108                                        connector, edid);
 109                        n = drm_add_edid_modes(connector, edid);
 110
 111                        omap_connector->hdmi_mode =
 112                                drm_detect_hdmi_monitor(edid);
 113                } else {
 114                        drm_mode_connector_update_edid_property(
 115                                        connector, NULL);
 116                }
 117
 118                kfree(edid);
 119        } else {
 120                struct drm_display_mode *mode = drm_mode_create(dev);
 121                struct videomode vm = {0};
 122
 123                dssdrv->get_timings(dssdev, &vm);
 124
 125                drm_display_mode_from_videomode(&vm, mode);
 126
 127                mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 128                drm_mode_set_name(mode);
 129                drm_mode_probed_add(connector, mode);
 130
 131                n = 1;
 132        }
 133
 134        return n;
 135}
 136
 137static int omap_connector_mode_valid(struct drm_connector *connector,
 138                                 struct drm_display_mode *mode)
 139{
 140        struct omap_connector *omap_connector = to_omap_connector(connector);
 141        struct omap_dss_device *dssdev = omap_connector->dssdev;
 142        struct omap_dss_driver *dssdrv = dssdev->driver;
 143        struct videomode vm = {0};
 144        struct drm_device *dev = connector->dev;
 145        struct drm_display_mode *new_mode;
 146        int r, ret = MODE_BAD;
 147
 148        drm_display_mode_to_videomode(mode, &vm);
 149        mode->vrefresh = drm_mode_vrefresh(mode);
 150
 151        /*
 152         * if the panel driver doesn't have a check_timings, it's most likely
 153         * a fixed resolution panel, check if the timings match with the
 154         * panel's timings
 155         */
 156        if (dssdrv->check_timings) {
 157                r = dssdrv->check_timings(dssdev, &vm);
 158        } else {
 159                struct videomode t = {0};
 160
 161                dssdrv->get_timings(dssdev, &t);
 162
 163                /*
 164                 * Ignore the flags, as we don't get them from
 165                 * drm_display_mode_to_videomode.
 166                 */
 167                t.flags = 0;
 168
 169                if (memcmp(&vm, &t, sizeof(vm)))
 170                        r = -EINVAL;
 171                else
 172                        r = 0;
 173        }
 174
 175        if (!r) {
 176                /* check if vrefresh is still valid */
 177                new_mode = drm_mode_duplicate(dev, mode);
 178                new_mode->clock = vm.pixelclock / 1000;
 179                new_mode->vrefresh = 0;
 180                if (mode->vrefresh == drm_mode_vrefresh(new_mode))
 181                        ret = MODE_OK;
 182                drm_mode_destroy(dev, new_mode);
 183        }
 184
 185        DBG("connector: mode %s: "
 186                        "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
 187                        (ret == MODE_OK) ? "valid" : "invalid",
 188                        mode->base.id, mode->name, mode->vrefresh, mode->clock,
 189                        mode->hdisplay, mode->hsync_start,
 190                        mode->hsync_end, mode->htotal,
 191                        mode->vdisplay, mode->vsync_start,
 192                        mode->vsync_end, mode->vtotal, mode->type, mode->flags);
 193
 194        return ret;
 195}
 196
 197static const struct drm_connector_funcs omap_connector_funcs = {
 198        .dpms = drm_atomic_helper_connector_dpms,
 199        .reset = drm_atomic_helper_connector_reset,
 200        .detect = omap_connector_detect,
 201        .fill_modes = drm_helper_probe_single_connector_modes,
 202        .destroy = omap_connector_destroy,
 203        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 204        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 205};
 206
 207static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
 208        .get_modes = omap_connector_get_modes,
 209        .mode_valid = omap_connector_mode_valid,
 210};
 211
 212/* initialize connector */
 213struct drm_connector *omap_connector_init(struct drm_device *dev,
 214                int connector_type, struct omap_dss_device *dssdev,
 215                struct drm_encoder *encoder)
 216{
 217        struct drm_connector *connector = NULL;
 218        struct omap_connector *omap_connector;
 219
 220        DBG("%s", dssdev->name);
 221
 222        omap_dss_get_device(dssdev);
 223
 224        omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
 225        if (!omap_connector)
 226                goto fail;
 227
 228        omap_connector->dssdev = dssdev;
 229
 230        connector = &omap_connector->base;
 231
 232        drm_connector_init(dev, connector, &omap_connector_funcs,
 233                                connector_type);
 234        drm_connector_helper_add(connector, &omap_connector_helper_funcs);
 235
 236        if (dssdev->driver->detect)
 237                connector->polled = DRM_CONNECTOR_POLL_CONNECT |
 238                                    DRM_CONNECTOR_POLL_DISCONNECT;
 239        else
 240                connector->polled = 0;
 241
 242        connector->interlace_allowed = 1;
 243        connector->doublescan_allowed = 0;
 244
 245        return connector;
 246
 247fail:
 248        if (connector)
 249                omap_connector_destroy(connector);
 250
 251        return NULL;
 252}
 253