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