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