Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at f2aeea57504cbbc58da3c59b939fc16150087648 684 lines 18 kB view raw
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_framebuffer.h> 18#include <drm/drm_gem_cma_helper.h> 19#include <drm/drm_plane_helper.h> 20#include <drm/drm_probe_helper.h> 21#include <drm/drm_simple_kms_helper.h> 22#include <drm/drm_vblank.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 563int shmob_drm_encoder_create(struct shmob_drm_device *sdev) 564{ 565 struct drm_encoder *encoder = &sdev->encoder.encoder; 566 int ret; 567 568 sdev->encoder.dpms = DRM_MODE_DPMS_OFF; 569 570 encoder->possible_crtcs = 1; 571 572 ret = drm_simple_encoder_init(sdev->ddev, encoder, 573 DRM_MODE_ENCODER_LVDS); 574 if (ret < 0) 575 return ret; 576 577 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 578 579 return 0; 580} 581 582/* ----------------------------------------------------------------------------- 583 * Connector 584 */ 585 586#define to_shmob_connector(c) \ 587 container_of(c, struct shmob_drm_connector, connector) 588 589static int shmob_drm_connector_get_modes(struct drm_connector *connector) 590{ 591 struct shmob_drm_device *sdev = connector->dev->dev_private; 592 struct drm_display_mode *mode; 593 594 mode = drm_mode_create(connector->dev); 595 if (mode == NULL) 596 return 0; 597 598 mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; 599 mode->clock = sdev->pdata->panel.mode.clock; 600 mode->hdisplay = sdev->pdata->panel.mode.hdisplay; 601 mode->hsync_start = sdev->pdata->panel.mode.hsync_start; 602 mode->hsync_end = sdev->pdata->panel.mode.hsync_end; 603 mode->htotal = sdev->pdata->panel.mode.htotal; 604 mode->vdisplay = sdev->pdata->panel.mode.vdisplay; 605 mode->vsync_start = sdev->pdata->panel.mode.vsync_start; 606 mode->vsync_end = sdev->pdata->panel.mode.vsync_end; 607 mode->vtotal = sdev->pdata->panel.mode.vtotal; 608 mode->flags = sdev->pdata->panel.mode.flags; 609 610 drm_mode_set_name(mode); 611 drm_mode_probed_add(connector, mode); 612 613 connector->display_info.width_mm = sdev->pdata->panel.width_mm; 614 connector->display_info.height_mm = sdev->pdata->panel.height_mm; 615 616 return 1; 617} 618 619static struct drm_encoder * 620shmob_drm_connector_best_encoder(struct drm_connector *connector) 621{ 622 struct shmob_drm_connector *scon = to_shmob_connector(connector); 623 624 return scon->encoder; 625} 626 627static const struct drm_connector_helper_funcs connector_helper_funcs = { 628 .get_modes = shmob_drm_connector_get_modes, 629 .best_encoder = shmob_drm_connector_best_encoder, 630}; 631 632static void shmob_drm_connector_destroy(struct drm_connector *connector) 633{ 634 struct shmob_drm_connector *scon = to_shmob_connector(connector); 635 636 shmob_drm_backlight_exit(scon); 637 drm_connector_unregister(connector); 638 drm_connector_cleanup(connector); 639} 640 641static const struct drm_connector_funcs connector_funcs = { 642 .dpms = drm_helper_connector_dpms, 643 .fill_modes = drm_helper_probe_single_connector_modes, 644 .destroy = shmob_drm_connector_destroy, 645}; 646 647int shmob_drm_connector_create(struct shmob_drm_device *sdev, 648 struct drm_encoder *encoder) 649{ 650 struct drm_connector *connector = &sdev->connector.connector; 651 int ret; 652 653 sdev->connector.encoder = encoder; 654 655 connector->display_info.width_mm = sdev->pdata->panel.width_mm; 656 connector->display_info.height_mm = sdev->pdata->panel.height_mm; 657 658 ret = drm_connector_init(sdev->ddev, connector, &connector_funcs, 659 DRM_MODE_CONNECTOR_LVDS); 660 if (ret < 0) 661 return ret; 662 663 drm_connector_helper_add(connector, &connector_helper_funcs); 664 665 ret = shmob_drm_backlight_init(&sdev->connector); 666 if (ret < 0) 667 goto err_cleanup; 668 669 ret = drm_connector_attach_encoder(connector, encoder); 670 if (ret < 0) 671 goto err_backlight; 672 673 drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); 674 drm_object_property_set_value(&connector->base, 675 sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); 676 677 return 0; 678 679err_backlight: 680 shmob_drm_backlight_exit(&sdev->connector); 681err_cleanup: 682 drm_connector_cleanup(connector); 683 return ret; 684}