linux/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
<<
>>
Prefs
   1/*
   2 * shmob_drm_crtc.c  --  SH Mobile DRM CRTCs
   3 *
   4 * Copyright (C) 2012 Renesas Electronics Corporation
   5 *
   6 * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/backlight.h>
  15#include <linux/clk.h>
  16
  17#include <drm/drmP.h>
  18#include <drm/drm_crtc.h>
  19#include <drm/drm_crtc_helper.h>
  20#include <drm/drm_fb_cma_helper.h>
  21#include <drm/drm_gem_cma_helper.h>
  22#include <drm/drm_plane_helper.h>
  23
  24#include "shmob_drm_backlight.h"
  25#include "shmob_drm_crtc.h"
  26#include "shmob_drm_drv.h"
  27#include "shmob_drm_kms.h"
  28#include "shmob_drm_plane.h"
  29#include "shmob_drm_regs.h"
  30
  31/*
  32 * TODO: panel support
  33 */
  34
  35/* -----------------------------------------------------------------------------
  36 * Clock management
  37 */
  38
  39static int shmob_drm_clk_on(struct shmob_drm_device *sdev)
  40{
  41        int ret;
  42
  43        if (sdev->clock) {
  44                ret = clk_prepare_enable(sdev->clock);
  45                if (ret < 0)
  46                        return ret;
  47        }
  48
  49        return 0;
  50}
  51
  52static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
  53{
  54        if (sdev->clock)
  55                clk_disable_unprepare(sdev->clock);
  56}
  57
  58/* -----------------------------------------------------------------------------
  59 * CRTC
  60 */
  61
  62static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
  63{
  64        struct drm_crtc *crtc = &scrtc->crtc;
  65        struct shmob_drm_device *sdev = crtc->dev->dev_private;
  66        const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
  67        const struct drm_display_mode *mode = &crtc->mode;
  68        u32 value;
  69
  70        value = sdev->ldmt1r
  71              | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL)
  72              | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL)
  73              | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0)
  74              | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0)
  75              | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0)
  76              | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0)
  77              | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0);
  78        lcdc_write(sdev, LDMT1R, value);
  79
  80        if (idata->interface >= SHMOB_DRM_IFACE_SYS8A &&
  81            idata->interface <= SHMOB_DRM_IFACE_SYS24) {
  82                /* Setup SYS bus. */
  83                value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT)
  84                      | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0)
  85                      | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0)
  86                      | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT)
  87                      | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT)
  88                      | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT);
  89                lcdc_write(sdev, LDMT2R, value);
  90
  91                value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT)
  92                      | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT)
  93                      | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT)
  94                      | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT);
  95                lcdc_write(sdev, LDMT3R, value);
  96        }
  97
  98        value = ((mode->hdisplay / 8) << 16)                    /* HDCN */
  99              | (mode->htotal / 8);                             /* HTCN */
 100        lcdc_write(sdev, LDHCNR, value);
 101
 102        value = (((mode->hsync_end - mode->hsync_start) / 8) << 16) /* HSYNW */
 103              | (mode->hsync_start / 8);                        /* HSYNP */
 104        lcdc_write(sdev, LDHSYNR, value);
 105
 106        value = ((mode->hdisplay & 7) << 24) | ((mode->htotal & 7) << 16)
 107              | (((mode->hsync_end - mode->hsync_start) & 7) << 8)
 108              | (mode->hsync_start & 7);
 109        lcdc_write(sdev, LDHAJR, value);
 110
 111        value = ((mode->vdisplay) << 16)                        /* VDLN */
 112              | mode->vtotal;                                   /* VTLN */
 113        lcdc_write(sdev, LDVLNR, value);
 114
 115        value = ((mode->vsync_end - mode->vsync_start) << 16)   /* VSYNW */
 116              | mode->vsync_start;                              /* VSYNP */
 117        lcdc_write(sdev, LDVSYNR, value);
 118}
 119
 120static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
 121{
 122        struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private;
 123        u32 value;
 124
 125        value = lcdc_read(sdev, LDCNT2R);
 126        if (start)
 127                lcdc_write(sdev, LDCNT2R, value | LDCNT2R_DO);
 128        else
 129                lcdc_write(sdev, LDCNT2R, value & ~LDCNT2R_DO);
 130
 131        /* Wait until power is applied/stopped. */
 132        while (1) {
 133                value = lcdc_read(sdev, LDPMR) & LDPMR_LPS;
 134                if ((start && value) || (!start && !value))
 135                        break;
 136
 137                cpu_relax();
 138        }
 139
 140        if (!start) {
 141                /* Stop the dot clock. */
 142                lcdc_write(sdev, LDDCKSTPR, LDDCKSTPR_DCKSTP);
 143        }
 144}
 145
 146/*
 147 * shmob_drm_crtc_start - Configure and start the LCDC
 148 * @scrtc: the SH Mobile CRTC
 149 *
 150 * Configure and start the LCDC device. External devices (clocks, MERAM, panels,
 151 * ...) are not touched by this function.
 152 */
 153static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
 154{
 155        struct drm_crtc *crtc = &scrtc->crtc;
 156        struct shmob_drm_device *sdev = crtc->dev->dev_private;
 157        const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
 158        const struct shmob_drm_format_info *format;
 159        struct drm_device *dev = sdev->ddev;
 160        struct drm_plane *plane;
 161        u32 value;
 162        int ret;
 163
 164        if (scrtc->started)
 165                return;
 166
 167        format = shmob_drm_format_info(crtc->primary->fb->format->format);
 168        if (WARN_ON(format == NULL))
 169                return;
 170
 171        /* Enable clocks before accessing the hardware. */
 172        ret = shmob_drm_clk_on(sdev);
 173        if (ret < 0)
 174                return;
 175
 176        /* Reset and enable the LCDC. */
 177        lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR);
 178        lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0);
 179        lcdc_write(sdev, LDCNT2R, LDCNT2R_ME);
 180
 181        /* Stop the LCDC first and disable all interrupts. */
 182        shmob_drm_crtc_start_stop(scrtc, false);
 183        lcdc_write(sdev, LDINTR, 0);
 184
 185        /* Configure power supply, dot clocks and start them. */
 186        lcdc_write(sdev, LDPMR, 0);
 187
 188        value = sdev->lddckr;
 189        if (idata->clk_div) {
 190                /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
 191                 * denominator.
 192                 */
 193                lcdc_write(sdev, LDDCKPAT1R, 0);
 194                lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1);
 195
 196                if (idata->clk_div == 1)
 197                        value |= LDDCKR_MOSEL;
 198                else
 199                        value |= idata->clk_div;
 200        }
 201
 202        lcdc_write(sdev, LDDCKR, value);
 203        lcdc_write(sdev, LDDCKSTPR, 0);
 204        lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0);
 205
 206        /* TODO: Setup SYS panel */
 207
 208        /* Setup geometry, format, frame buffer memory and operation mode. */
 209        shmob_drm_crtc_setup_geometry(scrtc);
 210
 211        /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
 212        lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1);
 213        lcdc_write(sdev, LDMLSR, scrtc->line_size);
 214        lcdc_write(sdev, LDSA1R, scrtc->dma[0]);
 215        if (format->yuv)
 216                lcdc_write(sdev, LDSA2R, scrtc->dma[1]);
 217        lcdc_write(sdev, LDSM1R, 0);
 218
 219        /* Word and long word swap. */
 220        switch (format->fourcc) {
 221        case DRM_FORMAT_RGB565:
 222        case DRM_FORMAT_NV21:
 223        case DRM_FORMAT_NV61:
 224        case DRM_FORMAT_NV42:
 225                value = LDDDSR_LS | LDDDSR_WS;
 226                break;
 227        case DRM_FORMAT_RGB888:
 228        case DRM_FORMAT_NV12:
 229        case DRM_FORMAT_NV16:
 230        case DRM_FORMAT_NV24:
 231                value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
 232                break;
 233        case DRM_FORMAT_ARGB8888:
 234        default:
 235                value = LDDDSR_LS;
 236                break;
 237        }
 238        lcdc_write(sdev, LDDDSR, value);
 239
 240        /* Setup planes. */
 241        drm_for_each_legacy_plane(plane, dev) {
 242                if (plane->crtc == crtc)
 243                        shmob_drm_plane_setup(plane);
 244        }
 245
 246        /* Enable the display output. */
 247        lcdc_write(sdev, LDCNT1R, LDCNT1R_DE);
 248
 249        shmob_drm_crtc_start_stop(scrtc, true);
 250
 251        scrtc->started = true;
 252}
 253
 254static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
 255{
 256        struct drm_crtc *crtc = &scrtc->crtc;
 257        struct shmob_drm_device *sdev = crtc->dev->dev_private;
 258
 259        if (!scrtc->started)
 260                return;
 261
 262        /* Stop the LCDC. */
 263        shmob_drm_crtc_start_stop(scrtc, false);
 264
 265        /* Disable the display output. */
 266        lcdc_write(sdev, LDCNT1R, 0);
 267
 268        /* Stop clocks. */
 269        shmob_drm_clk_off(sdev);
 270
 271        scrtc->started = false;
 272}
 273
 274void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc)
 275{
 276        shmob_drm_crtc_stop(scrtc);
 277}
 278
 279void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc)
 280{
 281        if (scrtc->dpms != DRM_MODE_DPMS_ON)
 282                return;
 283
 284        shmob_drm_crtc_start(scrtc);
 285}
 286
 287static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
 288                                        int x, int y)
 289{
 290        struct drm_crtc *crtc = &scrtc->crtc;
 291        struct drm_framebuffer *fb = crtc->primary->fb;
 292        struct drm_gem_cma_object *gem;
 293        unsigned int bpp;
 294
 295        bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp;
 296        gem = drm_fb_cma_get_gem_obj(fb, 0);
 297        scrtc->dma[0] = gem->paddr + fb->offsets[0]
 298                      + y * fb->pitches[0] + x * bpp / 8;
 299
 300        if (scrtc->format->yuv) {
 301                bpp = scrtc->format->bpp - 8;
 302                gem = drm_fb_cma_get_gem_obj(fb, 1);
 303                scrtc->dma[1] = gem->paddr + fb->offsets[1]
 304                              + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
 305                              + x * (bpp == 16 ? 2 : 1);
 306        }
 307}
 308
 309static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
 310{
 311        struct drm_crtc *crtc = &scrtc->crtc;
 312        struct shmob_drm_device *sdev = crtc->dev->dev_private;
 313
 314        shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y);
 315
 316        lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]);
 317        if (scrtc->format->yuv)
 318                lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]);
 319
 320        lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
 321}
 322
 323#define to_shmob_crtc(c)        container_of(c, struct shmob_drm_crtc, crtc)
 324
 325static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
 326{
 327        struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
 328
 329        if (scrtc->dpms == mode)
 330                return;
 331
 332        if (mode == DRM_MODE_DPMS_ON)
 333                shmob_drm_crtc_start(scrtc);
 334        else
 335                shmob_drm_crtc_stop(scrtc);
 336
 337        scrtc->dpms = mode;
 338}
 339
 340static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc)
 341{
 342        shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
 343}
 344
 345static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc,
 346                                   struct drm_display_mode *mode,
 347                                   struct drm_display_mode *adjusted_mode,
 348                                   int x, int y,
 349                                   struct drm_framebuffer *old_fb)
 350{
 351        struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
 352        struct shmob_drm_device *sdev = crtc->dev->dev_private;
 353        const struct shmob_drm_format_info *format;
 354
 355        format = shmob_drm_format_info(crtc->primary->fb->format->format);
 356        if (format == NULL) {
 357                dev_dbg(sdev->dev, "mode_set: unsupported format %08x\n",
 358                        crtc->primary->fb->format->format);
 359                return -EINVAL;
 360        }
 361
 362        scrtc->format = format;
 363        scrtc->line_size = crtc->primary->fb->pitches[0];
 364
 365        shmob_drm_crtc_compute_base(scrtc, x, y);
 366
 367        return 0;
 368}
 369
 370static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc)
 371{
 372        shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
 373}
 374
 375static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 376                                        struct drm_framebuffer *old_fb)
 377{
 378        shmob_drm_crtc_update_base(to_shmob_crtc(crtc));
 379
 380        return 0;
 381}
 382
 383static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
 384        .dpms = shmob_drm_crtc_dpms,
 385        .prepare = shmob_drm_crtc_mode_prepare,
 386        .commit = shmob_drm_crtc_mode_commit,
 387        .mode_set = shmob_drm_crtc_mode_set,
 388        .mode_set_base = shmob_drm_crtc_mode_set_base,
 389};
 390
 391void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
 392{
 393        struct drm_pending_vblank_event *event;
 394        struct drm_device *dev = scrtc->crtc.dev;
 395        unsigned long flags;
 396
 397        spin_lock_irqsave(&dev->event_lock, flags);
 398        event = scrtc->event;
 399        scrtc->event = NULL;
 400        if (event) {
 401                drm_crtc_send_vblank_event(&scrtc->crtc, event);
 402                drm_crtc_vblank_put(&scrtc->crtc);
 403        }
 404        spin_unlock_irqrestore(&dev->event_lock, flags);
 405}
 406
 407static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
 408                                    struct drm_framebuffer *fb,
 409                                    struct drm_pending_vblank_event *event,
 410                                    uint32_t page_flip_flags,
 411                                    struct drm_modeset_acquire_ctx *ctx)
 412{
 413        struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
 414        struct drm_device *dev = scrtc->crtc.dev;
 415        unsigned long flags;
 416
 417        spin_lock_irqsave(&dev->event_lock, flags);
 418        if (scrtc->event != NULL) {
 419                spin_unlock_irqrestore(&dev->event_lock, flags);
 420                return -EBUSY;
 421        }
 422        spin_unlock_irqrestore(&dev->event_lock, flags);
 423
 424        crtc->primary->fb = fb;
 425        shmob_drm_crtc_update_base(scrtc);
 426
 427        if (event) {
 428                event->pipe = 0;
 429                drm_crtc_vblank_get(&scrtc->crtc);
 430                spin_lock_irqsave(&dev->event_lock, flags);
 431                scrtc->event = event;
 432                spin_unlock_irqrestore(&dev->event_lock, flags);
 433        }
 434
 435        return 0;
 436}
 437
 438static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev,
 439                                         bool enable)
 440{
 441        unsigned long flags;
 442        u32 ldintr;
 443
 444        /* Be careful not to acknowledge any pending interrupt. */
 445        spin_lock_irqsave(&sdev->irq_lock, flags);
 446        ldintr = lcdc_read(sdev, LDINTR) | LDINTR_STATUS_MASK;
 447        if (enable)
 448                ldintr |= LDINTR_VEE;
 449        else
 450                ldintr &= ~LDINTR_VEE;
 451        lcdc_write(sdev, LDINTR, ldintr);
 452        spin_unlock_irqrestore(&sdev->irq_lock, flags);
 453}
 454
 455static int shmob_drm_enable_vblank(struct drm_crtc *crtc)
 456{
 457        struct shmob_drm_device *sdev = crtc->dev->dev_private;
 458
 459        shmob_drm_crtc_enable_vblank(sdev, true);
 460
 461        return 0;
 462}
 463
 464static void shmob_drm_disable_vblank(struct drm_crtc *crtc)
 465{
 466        struct shmob_drm_device *sdev = crtc->dev->dev_private;
 467
 468        shmob_drm_crtc_enable_vblank(sdev, false);
 469}
 470
 471static const struct drm_crtc_funcs crtc_funcs = {
 472        .destroy = drm_crtc_cleanup,
 473        .set_config = drm_crtc_helper_set_config,
 474        .page_flip = shmob_drm_crtc_page_flip,
 475        .enable_vblank = shmob_drm_enable_vblank,
 476        .disable_vblank = shmob_drm_disable_vblank,
 477};
 478
 479int shmob_drm_crtc_create(struct shmob_drm_device *sdev)
 480{
 481        struct drm_crtc *crtc = &sdev->crtc.crtc;
 482        int ret;
 483
 484        sdev->crtc.dpms = DRM_MODE_DPMS_OFF;
 485
 486        ret = drm_crtc_init(sdev->ddev, crtc, &crtc_funcs);
 487        if (ret < 0)
 488                return ret;
 489
 490        drm_crtc_helper_add(crtc, &crtc_helper_funcs);
 491
 492        return 0;
 493}
 494
 495/* -----------------------------------------------------------------------------
 496 * Encoder
 497 */
 498
 499#define to_shmob_encoder(e) \
 500        container_of(e, struct shmob_drm_encoder, encoder)
 501
 502static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
 503{
 504        struct shmob_drm_encoder *senc = to_shmob_encoder(encoder);
 505        struct shmob_drm_device *sdev = encoder->dev->dev_private;
 506        struct shmob_drm_connector *scon = &sdev->connector;
 507
 508        if (senc->dpms == mode)
 509                return;
 510
 511        shmob_drm_backlight_dpms(scon, mode);
 512
 513        senc->dpms = mode;
 514}
 515
 516static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
 517                                         const struct drm_display_mode *mode,
 518                                         struct drm_display_mode *adjusted_mode)
 519{
 520        struct drm_device *dev = encoder->dev;
 521        struct shmob_drm_device *sdev = dev->dev_private;
 522        struct drm_connector *connector = &sdev->connector.connector;
 523        const struct drm_display_mode *panel_mode;
 524
 525        if (list_empty(&connector->modes)) {
 526                dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
 527                return false;
 528        }
 529
 530        /* The flat panel mode is fixed, just copy it to the adjusted mode. */
 531        panel_mode = list_first_entry(&connector->modes,
 532                                      struct drm_display_mode, head);
 533        drm_mode_copy(adjusted_mode, panel_mode);
 534
 535        return true;
 536}
 537
 538static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder)
 539{
 540        /* No-op, everything is handled in the CRTC code. */
 541}
 542
 543static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder,
 544                                       struct drm_display_mode *mode,
 545                                       struct drm_display_mode *adjusted_mode)
 546{
 547        /* No-op, everything is handled in the CRTC code. */
 548}
 549
 550static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder)
 551{
 552        /* No-op, everything is handled in the CRTC code. */
 553}
 554
 555static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
 556        .dpms = shmob_drm_encoder_dpms,
 557        .mode_fixup = shmob_drm_encoder_mode_fixup,
 558        .prepare = shmob_drm_encoder_mode_prepare,
 559        .commit = shmob_drm_encoder_mode_commit,
 560        .mode_set = shmob_drm_encoder_mode_set,
 561};
 562
 563static void shmob_drm_encoder_destroy(struct drm_encoder *encoder)
 564{
 565        drm_encoder_cleanup(encoder);
 566}
 567
 568static const struct drm_encoder_funcs encoder_funcs = {
 569        .destroy = shmob_drm_encoder_destroy,
 570};
 571
 572int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
 573{
 574        struct drm_encoder *encoder = &sdev->encoder.encoder;
 575        int ret;
 576
 577        sdev->encoder.dpms = DRM_MODE_DPMS_OFF;
 578
 579        encoder->possible_crtcs = 1;
 580
 581        ret = drm_encoder_init(sdev->ddev, encoder, &encoder_funcs,
 582                               DRM_MODE_ENCODER_LVDS, NULL);
 583        if (ret < 0)
 584                return ret;
 585
 586        drm_encoder_helper_add(encoder, &encoder_helper_funcs);
 587
 588        return 0;
 589}
 590
 591/* -----------------------------------------------------------------------------
 592 * Connector
 593 */
 594
 595#define to_shmob_connector(c) \
 596        container_of(c, struct shmob_drm_connector, connector)
 597
 598static int shmob_drm_connector_get_modes(struct drm_connector *connector)
 599{
 600        struct shmob_drm_device *sdev = connector->dev->dev_private;
 601        struct drm_display_mode *mode;
 602
 603        mode = drm_mode_create(connector->dev);
 604        if (mode == NULL)
 605                return 0;
 606
 607        mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
 608        mode->clock = sdev->pdata->panel.mode.clock;
 609        mode->hdisplay = sdev->pdata->panel.mode.hdisplay;
 610        mode->hsync_start = sdev->pdata->panel.mode.hsync_start;
 611        mode->hsync_end = sdev->pdata->panel.mode.hsync_end;
 612        mode->htotal = sdev->pdata->panel.mode.htotal;
 613        mode->vdisplay = sdev->pdata->panel.mode.vdisplay;
 614        mode->vsync_start = sdev->pdata->panel.mode.vsync_start;
 615        mode->vsync_end = sdev->pdata->panel.mode.vsync_end;
 616        mode->vtotal = sdev->pdata->panel.mode.vtotal;
 617        mode->flags = sdev->pdata->panel.mode.flags;
 618
 619        drm_mode_set_name(mode);
 620        drm_mode_probed_add(connector, mode);
 621
 622        connector->display_info.width_mm = sdev->pdata->panel.width_mm;
 623        connector->display_info.height_mm = sdev->pdata->panel.height_mm;
 624
 625        return 1;
 626}
 627
 628static struct drm_encoder *
 629shmob_drm_connector_best_encoder(struct drm_connector *connector)
 630{
 631        struct shmob_drm_connector *scon = to_shmob_connector(connector);
 632
 633        return scon->encoder;
 634}
 635
 636static const struct drm_connector_helper_funcs connector_helper_funcs = {
 637        .get_modes = shmob_drm_connector_get_modes,
 638        .best_encoder = shmob_drm_connector_best_encoder,
 639};
 640
 641static void shmob_drm_connector_destroy(struct drm_connector *connector)
 642{
 643        struct shmob_drm_connector *scon = to_shmob_connector(connector);
 644
 645        shmob_drm_backlight_exit(scon);
 646        drm_connector_unregister(connector);
 647        drm_connector_cleanup(connector);
 648}
 649
 650static const struct drm_connector_funcs connector_funcs = {
 651        .dpms = drm_helper_connector_dpms,
 652        .fill_modes = drm_helper_probe_single_connector_modes,
 653        .destroy = shmob_drm_connector_destroy,
 654};
 655
 656int shmob_drm_connector_create(struct shmob_drm_device *sdev,
 657                               struct drm_encoder *encoder)
 658{
 659        struct drm_connector *connector = &sdev->connector.connector;
 660        int ret;
 661
 662        sdev->connector.encoder = encoder;
 663
 664        connector->display_info.width_mm = sdev->pdata->panel.width_mm;
 665        connector->display_info.height_mm = sdev->pdata->panel.height_mm;
 666
 667        ret = drm_connector_init(sdev->ddev, connector, &connector_funcs,
 668                                 DRM_MODE_CONNECTOR_LVDS);
 669        if (ret < 0)
 670                return ret;
 671
 672        drm_connector_helper_add(connector, &connector_helper_funcs);
 673
 674        ret = shmob_drm_backlight_init(&sdev->connector);
 675        if (ret < 0)
 676                goto err_cleanup;
 677
 678        ret = drm_connector_attach_encoder(connector, encoder);
 679        if (ret < 0)
 680                goto err_backlight;
 681
 682        drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
 683        drm_object_property_set_value(&connector->base,
 684                sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
 685
 686        return 0;
 687
 688err_backlight:
 689        shmob_drm_backlight_exit(&sdev->connector);
 690err_cleanup:
 691        drm_connector_cleanup(connector);
 692        return ret;
 693}
 694