linux/drivers/gpu/drm/exynos/exynos_drm_connector.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
   3 * Authors:
   4 *      Inki Dae <inki.dae@samsung.com>
   5 *      Joonyoung Shim <jy0922.shim@samsung.com>
   6 *      Seung-Woo Kim <sw0312.kim@samsung.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <drm/drmP.h>
  15#include <drm/drm_crtc_helper.h>
  16
  17#include <drm/exynos_drm.h>
  18#include "exynos_drm_drv.h"
  19#include "exynos_drm_encoder.h"
  20
  21#define to_exynos_connector(x)  container_of(x, struct exynos_drm_connector,\
  22                                drm_connector)
  23
  24struct exynos_drm_connector {
  25        struct drm_connector    drm_connector;
  26        uint32_t                encoder_id;
  27        struct exynos_drm_manager *manager;
  28        uint32_t                dpms;
  29};
  30
  31/* convert exynos_video_timings to drm_display_mode */
  32static inline void
  33convert_to_display_mode(struct drm_display_mode *mode,
  34                        struct exynos_drm_panel_info *panel)
  35{
  36        struct fb_videomode *timing = &panel->timing;
  37        DRM_DEBUG_KMS("%s\n", __FILE__);
  38
  39        mode->clock = timing->pixclock / 1000;
  40        mode->vrefresh = timing->refresh;
  41
  42        mode->hdisplay = timing->xres;
  43        mode->hsync_start = mode->hdisplay + timing->right_margin;
  44        mode->hsync_end = mode->hsync_start + timing->hsync_len;
  45        mode->htotal = mode->hsync_end + timing->left_margin;
  46
  47        mode->vdisplay = timing->yres;
  48        mode->vsync_start = mode->vdisplay + timing->lower_margin;
  49        mode->vsync_end = mode->vsync_start + timing->vsync_len;
  50        mode->vtotal = mode->vsync_end + timing->upper_margin;
  51        mode->width_mm = panel->width_mm;
  52        mode->height_mm = panel->height_mm;
  53
  54        if (timing->vmode & FB_VMODE_INTERLACED)
  55                mode->flags |= DRM_MODE_FLAG_INTERLACE;
  56
  57        if (timing->vmode & FB_VMODE_DOUBLE)
  58                mode->flags |= DRM_MODE_FLAG_DBLSCAN;
  59}
  60
  61/* convert drm_display_mode to exynos_video_timings */
  62static inline void
  63convert_to_video_timing(struct fb_videomode *timing,
  64                        struct drm_display_mode *mode)
  65{
  66        DRM_DEBUG_KMS("%s\n", __FILE__);
  67
  68        memset(timing, 0, sizeof(*timing));
  69
  70        timing->pixclock = mode->clock * 1000;
  71        timing->refresh = drm_mode_vrefresh(mode);
  72
  73        timing->xres = mode->hdisplay;
  74        timing->right_margin = mode->hsync_start - mode->hdisplay;
  75        timing->hsync_len = mode->hsync_end - mode->hsync_start;
  76        timing->left_margin = mode->htotal - mode->hsync_end;
  77
  78        timing->yres = mode->vdisplay;
  79        timing->lower_margin = mode->vsync_start - mode->vdisplay;
  80        timing->vsync_len = mode->vsync_end - mode->vsync_start;
  81        timing->upper_margin = mode->vtotal - mode->vsync_end;
  82
  83        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
  84                timing->vmode = FB_VMODE_INTERLACED;
  85        else
  86                timing->vmode = FB_VMODE_NONINTERLACED;
  87
  88        if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
  89                timing->vmode |= FB_VMODE_DOUBLE;
  90}
  91
  92static int exynos_drm_connector_get_modes(struct drm_connector *connector)
  93{
  94        struct exynos_drm_connector *exynos_connector =
  95                                        to_exynos_connector(connector);
  96        struct exynos_drm_manager *manager = exynos_connector->manager;
  97        struct exynos_drm_display_ops *display_ops = manager->display_ops;
  98        struct edid *edid = NULL;
  99        unsigned int count = 0;
 100        int ret;
 101
 102        DRM_DEBUG_KMS("%s\n", __FILE__);
 103
 104        if (!display_ops) {
 105                DRM_DEBUG_KMS("display_ops is null.\n");
 106                return 0;
 107        }
 108
 109        /*
 110         * if get_edid() exists then get_edid() callback of hdmi side
 111         * is called to get edid data through i2c interface else
 112         * get timing from the FIMD driver(display controller).
 113         *
 114         * P.S. in case of lcd panel, count is always 1 if success
 115         * because lcd panel has only one mode.
 116         */
 117        if (display_ops->get_edid) {
 118                edid = display_ops->get_edid(manager->dev, connector);
 119                if (IS_ERR_OR_NULL(edid)) {
 120                        ret = PTR_ERR(edid);
 121                        edid = NULL;
 122                        DRM_ERROR("Panel operation get_edid failed %d\n", ret);
 123                        goto out;
 124                }
 125
 126                count = drm_add_edid_modes(connector, edid);
 127                if (!count) {
 128                        DRM_ERROR("Add edid modes failed %d\n", count);
 129                        goto out;
 130                }
 131
 132                drm_mode_connector_update_edid_property(connector, edid);
 133        } else {
 134                struct exynos_drm_panel_info *panel;
 135                struct drm_display_mode *mode = drm_mode_create(connector->dev);
 136                if (!mode) {
 137                        DRM_ERROR("failed to create a new display mode.\n");
 138                        return 0;
 139                }
 140
 141                if (display_ops->get_panel)
 142                        panel = display_ops->get_panel(manager->dev);
 143                else {
 144                        drm_mode_destroy(connector->dev, mode);
 145                        return 0;
 146                }
 147
 148                convert_to_display_mode(mode, panel);
 149                connector->display_info.width_mm = mode->width_mm;
 150                connector->display_info.height_mm = mode->height_mm;
 151
 152                mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 153                drm_mode_set_name(mode);
 154                drm_mode_probed_add(connector, mode);
 155
 156                count = 1;
 157        }
 158
 159out:
 160        kfree(edid);
 161        return count;
 162}
 163
 164static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
 165                                            struct drm_display_mode *mode)
 166{
 167        struct exynos_drm_connector *exynos_connector =
 168                                        to_exynos_connector(connector);
 169        struct exynos_drm_manager *manager = exynos_connector->manager;
 170        struct exynos_drm_display_ops *display_ops = manager->display_ops;
 171        struct fb_videomode timing;
 172        int ret = MODE_BAD;
 173
 174        DRM_DEBUG_KMS("%s\n", __FILE__);
 175
 176        convert_to_video_timing(&timing, mode);
 177
 178        if (display_ops && display_ops->check_timing)
 179                if (!display_ops->check_timing(manager->dev, (void *)&timing))
 180                        ret = MODE_OK;
 181
 182        return ret;
 183}
 184
 185struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
 186{
 187        struct drm_device *dev = connector->dev;
 188        struct exynos_drm_connector *exynos_connector =
 189                                        to_exynos_connector(connector);
 190        struct drm_mode_object *obj;
 191        struct drm_encoder *encoder;
 192
 193        DRM_DEBUG_KMS("%s\n", __FILE__);
 194
 195        obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
 196                                   DRM_MODE_OBJECT_ENCODER);
 197        if (!obj) {
 198                DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
 199                                exynos_connector->encoder_id);
 200                return NULL;
 201        }
 202
 203        encoder = obj_to_encoder(obj);
 204
 205        return encoder;
 206}
 207
 208static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
 209        .get_modes      = exynos_drm_connector_get_modes,
 210        .mode_valid     = exynos_drm_connector_mode_valid,
 211        .best_encoder   = exynos_drm_best_encoder,
 212};
 213
 214void exynos_drm_display_power(struct drm_connector *connector, int mode)
 215{
 216        struct drm_encoder *encoder = exynos_drm_best_encoder(connector);
 217        struct exynos_drm_connector *exynos_connector;
 218        struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
 219        struct exynos_drm_display_ops *display_ops = manager->display_ops;
 220
 221        exynos_connector = to_exynos_connector(connector);
 222
 223        if (exynos_connector->dpms == mode) {
 224                DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
 225                return;
 226        }
 227
 228        if (display_ops && display_ops->power_on)
 229                display_ops->power_on(manager->dev, mode);
 230
 231        exynos_connector->dpms = mode;
 232}
 233
 234static void exynos_drm_connector_dpms(struct drm_connector *connector,
 235                                        int mode)
 236{
 237        DRM_DEBUG_KMS("%s\n", __FILE__);
 238
 239        /*
 240         * in case that drm_crtc_helper_set_mode() is called,
 241         * encoder/crtc->funcs->dpms() will be just returned
 242         * because they already were DRM_MODE_DPMS_ON so only
 243         * exynos_drm_display_power() will be called.
 244         */
 245        drm_helper_connector_dpms(connector, mode);
 246
 247        exynos_drm_display_power(connector, mode);
 248
 249}
 250
 251static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
 252                                unsigned int max_width, unsigned int max_height)
 253{
 254        struct exynos_drm_connector *exynos_connector =
 255                                        to_exynos_connector(connector);
 256        struct exynos_drm_manager *manager = exynos_connector->manager;
 257        struct exynos_drm_manager_ops *ops = manager->ops;
 258        unsigned int width, height;
 259
 260        width = max_width;
 261        height = max_height;
 262
 263        /*
 264         * if specific driver want to find desired_mode using maxmum
 265         * resolution then get max width and height from that driver.
 266         */
 267        if (ops && ops->get_max_resol)
 268                ops->get_max_resol(manager->dev, &width, &height);
 269
 270        return drm_helper_probe_single_connector_modes(connector, width,
 271                                                        height);
 272}
 273
 274/* get detection status of display device. */
 275static enum drm_connector_status
 276exynos_drm_connector_detect(struct drm_connector *connector, bool force)
 277{
 278        struct exynos_drm_connector *exynos_connector =
 279                                        to_exynos_connector(connector);
 280        struct exynos_drm_manager *manager = exynos_connector->manager;
 281        struct exynos_drm_display_ops *display_ops =
 282                                        manager->display_ops;
 283        enum drm_connector_status status = connector_status_disconnected;
 284
 285        DRM_DEBUG_KMS("%s\n", __FILE__);
 286
 287        if (display_ops && display_ops->is_connected) {
 288                if (display_ops->is_connected(manager->dev))
 289                        status = connector_status_connected;
 290                else
 291                        status = connector_status_disconnected;
 292        }
 293
 294        return status;
 295}
 296
 297static void exynos_drm_connector_destroy(struct drm_connector *connector)
 298{
 299        struct exynos_drm_connector *exynos_connector =
 300                to_exynos_connector(connector);
 301
 302        DRM_DEBUG_KMS("%s\n", __FILE__);
 303
 304        drm_sysfs_connector_remove(connector);
 305        drm_connector_cleanup(connector);
 306        kfree(exynos_connector);
 307}
 308
 309static struct drm_connector_funcs exynos_connector_funcs = {
 310        .dpms           = exynos_drm_connector_dpms,
 311        .fill_modes     = exynos_drm_connector_fill_modes,
 312        .detect         = exynos_drm_connector_detect,
 313        .destroy        = exynos_drm_connector_destroy,
 314};
 315
 316struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
 317                                                   struct drm_encoder *encoder)
 318{
 319        struct exynos_drm_connector *exynos_connector;
 320        struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
 321        struct drm_connector *connector;
 322        int type;
 323        int err;
 324
 325        DRM_DEBUG_KMS("%s\n", __FILE__);
 326
 327        exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
 328        if (!exynos_connector) {
 329                DRM_ERROR("failed to allocate connector\n");
 330                return NULL;
 331        }
 332
 333        connector = &exynos_connector->drm_connector;
 334
 335        switch (manager->display_ops->type) {
 336        case EXYNOS_DISPLAY_TYPE_HDMI:
 337                type = DRM_MODE_CONNECTOR_HDMIA;
 338                connector->interlace_allowed = true;
 339                connector->polled = DRM_CONNECTOR_POLL_HPD;
 340                break;
 341        case EXYNOS_DISPLAY_TYPE_VIDI:
 342                type = DRM_MODE_CONNECTOR_VIRTUAL;
 343                connector->polled = DRM_CONNECTOR_POLL_HPD;
 344                break;
 345        default:
 346                type = DRM_MODE_CONNECTOR_Unknown;
 347                break;
 348        }
 349
 350        drm_connector_init(dev, connector, &exynos_connector_funcs, type);
 351        drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
 352
 353        err = drm_sysfs_connector_add(connector);
 354        if (err)
 355                goto err_connector;
 356
 357        exynos_connector->encoder_id = encoder->base.id;
 358        exynos_connector->manager = manager;
 359        exynos_connector->dpms = DRM_MODE_DPMS_OFF;
 360        connector->dpms = DRM_MODE_DPMS_OFF;
 361        connector->encoder = encoder;
 362
 363        err = drm_mode_connector_attach_encoder(connector, encoder);
 364        if (err) {
 365                DRM_ERROR("failed to attach a connector to a encoder\n");
 366                goto err_sysfs;
 367        }
 368
 369        DRM_DEBUG_KMS("connector has been created\n");
 370
 371        return connector;
 372
 373err_sysfs:
 374        drm_sysfs_connector_remove(connector);
 375err_connector:
 376        drm_connector_cleanup(connector);
 377        kfree(exynos_connector);
 378        return NULL;
 379}
 380