0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/backlight.h>
0011 #include <linux/clk.h>
0012
0013 #include <drm/drm_crtc.h>
0014 #include <drm/drm_crtc_helper.h>
0015 #include <drm/drm_fb_cma_helper.h>
0016 #include <drm/drm_fourcc.h>
0017 #include <drm/drm_framebuffer.h>
0018 #include <drm/drm_gem_cma_helper.h>
0019 #include <drm/drm_plane_helper.h>
0020 #include <drm/drm_probe_helper.h>
0021 #include <drm/drm_simple_kms_helper.h>
0022 #include <drm/drm_vblank.h>
0023
0024 #include "shmob_drm_backlight.h"
0025 #include "shmob_drm_crtc.h"
0026 #include "shmob_drm_drv.h"
0027 #include "shmob_drm_kms.h"
0028 #include "shmob_drm_plane.h"
0029 #include "shmob_drm_regs.h"
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039 static int shmob_drm_clk_on(struct shmob_drm_device *sdev)
0040 {
0041 int ret;
0042
0043 if (sdev->clock) {
0044 ret = clk_prepare_enable(sdev->clock);
0045 if (ret < 0)
0046 return ret;
0047 }
0048
0049 return 0;
0050 }
0051
0052 static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
0053 {
0054 if (sdev->clock)
0055 clk_disable_unprepare(sdev->clock);
0056 }
0057
0058
0059
0060
0061
0062 static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
0063 {
0064 struct drm_crtc *crtc = &scrtc->crtc;
0065 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0066 const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
0067 const struct drm_display_mode *mode = &crtc->mode;
0068 u32 value;
0069
0070 value = sdev->ldmt1r
0071 | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL)
0072 | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL)
0073 | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0)
0074 | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0)
0075 | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0)
0076 | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0)
0077 | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0);
0078 lcdc_write(sdev, LDMT1R, value);
0079
0080 if (idata->interface >= SHMOB_DRM_IFACE_SYS8A &&
0081 idata->interface <= SHMOB_DRM_IFACE_SYS24) {
0082
0083 value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT)
0084 | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0)
0085 | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0)
0086 | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT)
0087 | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT)
0088 | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT);
0089 lcdc_write(sdev, LDMT2R, value);
0090
0091 value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT)
0092 | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT)
0093 | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT)
0094 | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT);
0095 lcdc_write(sdev, LDMT3R, value);
0096 }
0097
0098 value = ((mode->hdisplay / 8) << 16)
0099 | (mode->htotal / 8);
0100 lcdc_write(sdev, LDHCNR, value);
0101
0102 value = (((mode->hsync_end - mode->hsync_start) / 8) << 16)
0103 | (mode->hsync_start / 8);
0104 lcdc_write(sdev, LDHSYNR, value);
0105
0106 value = ((mode->hdisplay & 7) << 24) | ((mode->htotal & 7) << 16)
0107 | (((mode->hsync_end - mode->hsync_start) & 7) << 8)
0108 | (mode->hsync_start & 7);
0109 lcdc_write(sdev, LDHAJR, value);
0110
0111 value = ((mode->vdisplay) << 16)
0112 | mode->vtotal;
0113 lcdc_write(sdev, LDVLNR, value);
0114
0115 value = ((mode->vsync_end - mode->vsync_start) << 16)
0116 | mode->vsync_start;
0117 lcdc_write(sdev, LDVSYNR, value);
0118 }
0119
0120 static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
0121 {
0122 struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private;
0123 u32 value;
0124
0125 value = lcdc_read(sdev, LDCNT2R);
0126 if (start)
0127 lcdc_write(sdev, LDCNT2R, value | LDCNT2R_DO);
0128 else
0129 lcdc_write(sdev, LDCNT2R, value & ~LDCNT2R_DO);
0130
0131
0132 while (1) {
0133 value = lcdc_read(sdev, LDPMR) & LDPMR_LPS;
0134 if ((start && value) || (!start && !value))
0135 break;
0136
0137 cpu_relax();
0138 }
0139
0140 if (!start) {
0141
0142 lcdc_write(sdev, LDDCKSTPR, LDDCKSTPR_DCKSTP);
0143 }
0144 }
0145
0146
0147
0148
0149
0150
0151
0152
0153 static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
0154 {
0155 struct drm_crtc *crtc = &scrtc->crtc;
0156 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0157 const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
0158 const struct shmob_drm_format_info *format;
0159 struct drm_device *dev = sdev->ddev;
0160 struct drm_plane *plane;
0161 u32 value;
0162 int ret;
0163
0164 if (scrtc->started)
0165 return;
0166
0167 format = shmob_drm_format_info(crtc->primary->fb->format->format);
0168 if (WARN_ON(format == NULL))
0169 return;
0170
0171
0172 ret = shmob_drm_clk_on(sdev);
0173 if (ret < 0)
0174 return;
0175
0176
0177 lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR);
0178 lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0);
0179 lcdc_write(sdev, LDCNT2R, LDCNT2R_ME);
0180
0181
0182 shmob_drm_crtc_start_stop(scrtc, false);
0183 lcdc_write(sdev, LDINTR, 0);
0184
0185
0186 lcdc_write(sdev, LDPMR, 0);
0187
0188 value = sdev->lddckr;
0189 if (idata->clk_div) {
0190
0191
0192
0193 lcdc_write(sdev, LDDCKPAT1R, 0);
0194 lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1);
0195
0196 if (idata->clk_div == 1)
0197 value |= LDDCKR_MOSEL;
0198 else
0199 value |= idata->clk_div;
0200 }
0201
0202 lcdc_write(sdev, LDDCKR, value);
0203 lcdc_write(sdev, LDDCKSTPR, 0);
0204 lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0);
0205
0206
0207
0208
0209 shmob_drm_crtc_setup_geometry(scrtc);
0210
0211
0212 lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1);
0213 lcdc_write(sdev, LDMLSR, scrtc->line_size);
0214 lcdc_write(sdev, LDSA1R, scrtc->dma[0]);
0215 if (format->yuv)
0216 lcdc_write(sdev, LDSA2R, scrtc->dma[1]);
0217 lcdc_write(sdev, LDSM1R, 0);
0218
0219
0220 switch (format->fourcc) {
0221 case DRM_FORMAT_RGB565:
0222 case DRM_FORMAT_NV21:
0223 case DRM_FORMAT_NV61:
0224 case DRM_FORMAT_NV42:
0225 value = LDDDSR_LS | LDDDSR_WS;
0226 break;
0227 case DRM_FORMAT_RGB888:
0228 case DRM_FORMAT_NV12:
0229 case DRM_FORMAT_NV16:
0230 case DRM_FORMAT_NV24:
0231 value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
0232 break;
0233 case DRM_FORMAT_ARGB8888:
0234 default:
0235 value = LDDDSR_LS;
0236 break;
0237 }
0238 lcdc_write(sdev, LDDDSR, value);
0239
0240
0241 drm_for_each_legacy_plane(plane, dev) {
0242 if (plane->crtc == crtc)
0243 shmob_drm_plane_setup(plane);
0244 }
0245
0246
0247 lcdc_write(sdev, LDCNT1R, LDCNT1R_DE);
0248
0249 shmob_drm_crtc_start_stop(scrtc, true);
0250
0251 scrtc->started = true;
0252 }
0253
0254 static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
0255 {
0256 struct drm_crtc *crtc = &scrtc->crtc;
0257 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0258
0259 if (!scrtc->started)
0260 return;
0261
0262
0263 shmob_drm_crtc_start_stop(scrtc, false);
0264
0265
0266 lcdc_write(sdev, LDCNT1R, 0);
0267
0268
0269 shmob_drm_clk_off(sdev);
0270
0271 scrtc->started = false;
0272 }
0273
0274 void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc)
0275 {
0276 shmob_drm_crtc_stop(scrtc);
0277 }
0278
0279 void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc)
0280 {
0281 if (scrtc->dpms != DRM_MODE_DPMS_ON)
0282 return;
0283
0284 shmob_drm_crtc_start(scrtc);
0285 }
0286
0287 static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
0288 int x, int y)
0289 {
0290 struct drm_crtc *crtc = &scrtc->crtc;
0291 struct drm_framebuffer *fb = crtc->primary->fb;
0292 struct drm_gem_cma_object *gem;
0293 unsigned int bpp;
0294
0295 bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp;
0296 gem = drm_fb_cma_get_gem_obj(fb, 0);
0297 scrtc->dma[0] = gem->paddr + fb->offsets[0]
0298 + y * fb->pitches[0] + x * bpp / 8;
0299
0300 if (scrtc->format->yuv) {
0301 bpp = scrtc->format->bpp - 8;
0302 gem = drm_fb_cma_get_gem_obj(fb, 1);
0303 scrtc->dma[1] = gem->paddr + fb->offsets[1]
0304 + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
0305 + x * (bpp == 16 ? 2 : 1);
0306 }
0307 }
0308
0309 static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
0310 {
0311 struct drm_crtc *crtc = &scrtc->crtc;
0312 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0313
0314 shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y);
0315
0316 lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]);
0317 if (scrtc->format->yuv)
0318 lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]);
0319
0320 lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
0321 }
0322
0323 #define to_shmob_crtc(c) container_of(c, struct shmob_drm_crtc, crtc)
0324
0325 static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
0326 {
0327 struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
0328
0329 if (scrtc->dpms == mode)
0330 return;
0331
0332 if (mode == DRM_MODE_DPMS_ON)
0333 shmob_drm_crtc_start(scrtc);
0334 else
0335 shmob_drm_crtc_stop(scrtc);
0336
0337 scrtc->dpms = mode;
0338 }
0339
0340 static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc)
0341 {
0342 shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
0343 }
0344
0345 static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc,
0346 struct drm_display_mode *mode,
0347 struct drm_display_mode *adjusted_mode,
0348 int x, int y,
0349 struct drm_framebuffer *old_fb)
0350 {
0351 struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
0352 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0353 const struct shmob_drm_format_info *format;
0354
0355 format = shmob_drm_format_info(crtc->primary->fb->format->format);
0356 if (format == NULL) {
0357 dev_dbg(sdev->dev, "mode_set: unsupported format %08x\n",
0358 crtc->primary->fb->format->format);
0359 return -EINVAL;
0360 }
0361
0362 scrtc->format = format;
0363 scrtc->line_size = crtc->primary->fb->pitches[0];
0364
0365 shmob_drm_crtc_compute_base(scrtc, x, y);
0366
0367 return 0;
0368 }
0369
0370 static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc)
0371 {
0372 shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
0373 }
0374
0375 static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
0376 struct drm_framebuffer *old_fb)
0377 {
0378 shmob_drm_crtc_update_base(to_shmob_crtc(crtc));
0379
0380 return 0;
0381 }
0382
0383 static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
0384 .dpms = shmob_drm_crtc_dpms,
0385 .prepare = shmob_drm_crtc_mode_prepare,
0386 .commit = shmob_drm_crtc_mode_commit,
0387 .mode_set = shmob_drm_crtc_mode_set,
0388 .mode_set_base = shmob_drm_crtc_mode_set_base,
0389 };
0390
0391 void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
0392 {
0393 struct drm_pending_vblank_event *event;
0394 struct drm_device *dev = scrtc->crtc.dev;
0395 unsigned long flags;
0396
0397 spin_lock_irqsave(&dev->event_lock, flags);
0398 event = scrtc->event;
0399 scrtc->event = NULL;
0400 if (event) {
0401 drm_crtc_send_vblank_event(&scrtc->crtc, event);
0402 drm_crtc_vblank_put(&scrtc->crtc);
0403 }
0404 spin_unlock_irqrestore(&dev->event_lock, flags);
0405 }
0406
0407 static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
0408 struct drm_framebuffer *fb,
0409 struct drm_pending_vblank_event *event,
0410 uint32_t page_flip_flags,
0411 struct drm_modeset_acquire_ctx *ctx)
0412 {
0413 struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
0414 struct drm_device *dev = scrtc->crtc.dev;
0415 unsigned long flags;
0416
0417 spin_lock_irqsave(&dev->event_lock, flags);
0418 if (scrtc->event != NULL) {
0419 spin_unlock_irqrestore(&dev->event_lock, flags);
0420 return -EBUSY;
0421 }
0422 spin_unlock_irqrestore(&dev->event_lock, flags);
0423
0424 crtc->primary->fb = fb;
0425 shmob_drm_crtc_update_base(scrtc);
0426
0427 if (event) {
0428 event->pipe = 0;
0429 drm_crtc_vblank_get(&scrtc->crtc);
0430 spin_lock_irqsave(&dev->event_lock, flags);
0431 scrtc->event = event;
0432 spin_unlock_irqrestore(&dev->event_lock, flags);
0433 }
0434
0435 return 0;
0436 }
0437
0438 static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev,
0439 bool enable)
0440 {
0441 unsigned long flags;
0442 u32 ldintr;
0443
0444
0445 spin_lock_irqsave(&sdev->irq_lock, flags);
0446 ldintr = lcdc_read(sdev, LDINTR) | LDINTR_STATUS_MASK;
0447 if (enable)
0448 ldintr |= LDINTR_VEE;
0449 else
0450 ldintr &= ~LDINTR_VEE;
0451 lcdc_write(sdev, LDINTR, ldintr);
0452 spin_unlock_irqrestore(&sdev->irq_lock, flags);
0453 }
0454
0455 static int shmob_drm_enable_vblank(struct drm_crtc *crtc)
0456 {
0457 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0458
0459 shmob_drm_crtc_enable_vblank(sdev, true);
0460
0461 return 0;
0462 }
0463
0464 static void shmob_drm_disable_vblank(struct drm_crtc *crtc)
0465 {
0466 struct shmob_drm_device *sdev = crtc->dev->dev_private;
0467
0468 shmob_drm_crtc_enable_vblank(sdev, false);
0469 }
0470
0471 static const struct drm_crtc_funcs crtc_funcs = {
0472 .destroy = drm_crtc_cleanup,
0473 .set_config = drm_crtc_helper_set_config,
0474 .page_flip = shmob_drm_crtc_page_flip,
0475 .enable_vblank = shmob_drm_enable_vblank,
0476 .disable_vblank = shmob_drm_disable_vblank,
0477 };
0478
0479 int shmob_drm_crtc_create(struct shmob_drm_device *sdev)
0480 {
0481 struct drm_crtc *crtc = &sdev->crtc.crtc;
0482 int ret;
0483
0484 sdev->crtc.dpms = DRM_MODE_DPMS_OFF;
0485
0486 ret = drm_crtc_init(sdev->ddev, crtc, &crtc_funcs);
0487 if (ret < 0)
0488 return ret;
0489
0490 drm_crtc_helper_add(crtc, &crtc_helper_funcs);
0491
0492 return 0;
0493 }
0494
0495
0496
0497
0498
0499 #define to_shmob_encoder(e) \
0500 container_of(e, struct shmob_drm_encoder, encoder)
0501
0502 static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
0503 {
0504 struct shmob_drm_encoder *senc = to_shmob_encoder(encoder);
0505 struct shmob_drm_device *sdev = encoder->dev->dev_private;
0506 struct shmob_drm_connector *scon = &sdev->connector;
0507
0508 if (senc->dpms == mode)
0509 return;
0510
0511 shmob_drm_backlight_dpms(scon, mode);
0512
0513 senc->dpms = mode;
0514 }
0515
0516 static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
0517 const struct drm_display_mode *mode,
0518 struct drm_display_mode *adjusted_mode)
0519 {
0520 struct drm_device *dev = encoder->dev;
0521 struct shmob_drm_device *sdev = dev->dev_private;
0522 struct drm_connector *connector = &sdev->connector.connector;
0523 const struct drm_display_mode *panel_mode;
0524
0525 if (list_empty(&connector->modes)) {
0526 dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
0527 return false;
0528 }
0529
0530
0531 panel_mode = list_first_entry(&connector->modes,
0532 struct drm_display_mode, head);
0533 drm_mode_copy(adjusted_mode, panel_mode);
0534
0535 return true;
0536 }
0537
0538 static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder)
0539 {
0540
0541 }
0542
0543 static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder,
0544 struct drm_display_mode *mode,
0545 struct drm_display_mode *adjusted_mode)
0546 {
0547
0548 }
0549
0550 static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder)
0551 {
0552
0553 }
0554
0555 static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
0556 .dpms = shmob_drm_encoder_dpms,
0557 .mode_fixup = shmob_drm_encoder_mode_fixup,
0558 .prepare = shmob_drm_encoder_mode_prepare,
0559 .commit = shmob_drm_encoder_mode_commit,
0560 .mode_set = shmob_drm_encoder_mode_set,
0561 };
0562
0563 int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
0564 {
0565 struct drm_encoder *encoder = &sdev->encoder.encoder;
0566 int ret;
0567
0568 sdev->encoder.dpms = DRM_MODE_DPMS_OFF;
0569
0570 encoder->possible_crtcs = 1;
0571
0572 ret = drm_simple_encoder_init(sdev->ddev, encoder,
0573 DRM_MODE_ENCODER_LVDS);
0574 if (ret < 0)
0575 return ret;
0576
0577 drm_encoder_helper_add(encoder, &encoder_helper_funcs);
0578
0579 return 0;
0580 }
0581
0582
0583
0584
0585
0586 #define to_shmob_connector(c) \
0587 container_of(c, struct shmob_drm_connector, connector)
0588
0589 static int shmob_drm_connector_get_modes(struct drm_connector *connector)
0590 {
0591 struct shmob_drm_device *sdev = connector->dev->dev_private;
0592 struct drm_display_mode *mode;
0593
0594 mode = drm_mode_create(connector->dev);
0595 if (mode == NULL)
0596 return 0;
0597
0598 mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
0599 mode->clock = sdev->pdata->panel.mode.clock;
0600 mode->hdisplay = sdev->pdata->panel.mode.hdisplay;
0601 mode->hsync_start = sdev->pdata->panel.mode.hsync_start;
0602 mode->hsync_end = sdev->pdata->panel.mode.hsync_end;
0603 mode->htotal = sdev->pdata->panel.mode.htotal;
0604 mode->vdisplay = sdev->pdata->panel.mode.vdisplay;
0605 mode->vsync_start = sdev->pdata->panel.mode.vsync_start;
0606 mode->vsync_end = sdev->pdata->panel.mode.vsync_end;
0607 mode->vtotal = sdev->pdata->panel.mode.vtotal;
0608 mode->flags = sdev->pdata->panel.mode.flags;
0609
0610 drm_mode_set_name(mode);
0611 drm_mode_probed_add(connector, mode);
0612
0613 connector->display_info.width_mm = sdev->pdata->panel.width_mm;
0614 connector->display_info.height_mm = sdev->pdata->panel.height_mm;
0615
0616 return 1;
0617 }
0618
0619 static struct drm_encoder *
0620 shmob_drm_connector_best_encoder(struct drm_connector *connector)
0621 {
0622 struct shmob_drm_connector *scon = to_shmob_connector(connector);
0623
0624 return scon->encoder;
0625 }
0626
0627 static const struct drm_connector_helper_funcs connector_helper_funcs = {
0628 .get_modes = shmob_drm_connector_get_modes,
0629 .best_encoder = shmob_drm_connector_best_encoder,
0630 };
0631
0632 static void shmob_drm_connector_destroy(struct drm_connector *connector)
0633 {
0634 struct shmob_drm_connector *scon = to_shmob_connector(connector);
0635
0636 shmob_drm_backlight_exit(scon);
0637 drm_connector_unregister(connector);
0638 drm_connector_cleanup(connector);
0639 }
0640
0641 static const struct drm_connector_funcs connector_funcs = {
0642 .dpms = drm_helper_connector_dpms,
0643 .fill_modes = drm_helper_probe_single_connector_modes,
0644 .destroy = shmob_drm_connector_destroy,
0645 };
0646
0647 int shmob_drm_connector_create(struct shmob_drm_device *sdev,
0648 struct drm_encoder *encoder)
0649 {
0650 struct drm_connector *connector = &sdev->connector.connector;
0651 int ret;
0652
0653 sdev->connector.encoder = encoder;
0654
0655 connector->display_info.width_mm = sdev->pdata->panel.width_mm;
0656 connector->display_info.height_mm = sdev->pdata->panel.height_mm;
0657
0658 ret = drm_connector_init(sdev->ddev, connector, &connector_funcs,
0659 DRM_MODE_CONNECTOR_LVDS);
0660 if (ret < 0)
0661 return ret;
0662
0663 drm_connector_helper_add(connector, &connector_helper_funcs);
0664
0665 ret = shmob_drm_backlight_init(&sdev->connector);
0666 if (ret < 0)
0667 goto err_cleanup;
0668
0669 ret = drm_connector_attach_encoder(connector, encoder);
0670 if (ret < 0)
0671 goto err_backlight;
0672
0673 drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
0674 drm_object_property_set_value(&connector->base,
0675 sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
0676
0677 return 0;
0678
0679 err_backlight:
0680 shmob_drm_backlight_exit(&sdev->connector);
0681 err_cleanup:
0682 drm_connector_cleanup(connector);
0683 return ret;
0684 }