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