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