1/************************************************************************** 2 * Copyright (c) 2011, Intel Corporation. 3 * All Rights Reserved. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms and conditions of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 17 * 18 **************************************************************************/ 19 20#include "psb_drv.h" 21#include "mid_bios.h" 22#include "mdfld_output.h" 23#include "mdfld_dsi_output.h" 24#include "tc35876x-dsi-lvds.h" 25 26#include <asm/intel_scu_ipc.h> 27 28#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE 29 30#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF 31#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ 32#define BLC_PWM_FREQ_CALC_CONSTANT 32 33#define MHz 1000000 34#define BRIGHTNESS_MIN_LEVEL 1 35#define BRIGHTNESS_MAX_LEVEL 100 36#define BRIGHTNESS_MASK 0xFF 37#define BLC_POLARITY_NORMAL 0 38#define BLC_POLARITY_INVERSE 1 39#define BLC_ADJUSTMENT_MAX 100 40 41#define MDFLD_BLC_PWM_PRECISION_FACTOR 10 42#define MDFLD_BLC_MAX_PWM_REG_FREQ 0xFFFE 43#define MDFLD_BLC_MIN_PWM_REG_FREQ 0x2 44 45#define MDFLD_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) 46#define MDFLD_BACKLIGHT_PWM_CTL_SHIFT (16) 47 48static struct backlight_device *mdfld_backlight_device; 49 50int mdfld_set_brightness(struct backlight_device *bd) 51{ 52 struct drm_device *dev = 53 (struct drm_device *)bl_get_data(mdfld_backlight_device); 54 struct drm_psb_private *dev_priv = dev->dev_private; 55 int level = bd->props.brightness; 56 57 DRM_DEBUG_DRIVER("backlight level set to %d\n", level); 58 59 /* Perform value bounds checking */ 60 if (level < BRIGHTNESS_MIN_LEVEL) 61 level = BRIGHTNESS_MIN_LEVEL; 62 63 if (gma_power_begin(dev, false)) { 64 u32 adjusted_level = 0; 65 66 /* 67 * Adjust the backlight level with the percent in 68 * dev_priv->blc_adj2 69 */ 70 adjusted_level = level * dev_priv->blc_adj2; 71 adjusted_level = adjusted_level / BLC_ADJUSTMENT_MAX; 72 dev_priv->brightness_adjusted = adjusted_level; 73 74 if (mdfld_get_panel_type(dev, 0) == TC35876X) { 75 if (dev_priv->dpi_panel_on[0] || 76 dev_priv->dpi_panel_on[2]) 77 tc35876x_brightness_control(dev, 78 dev_priv->brightness_adjusted); 79 } else { 80 if (dev_priv->dpi_panel_on[0]) 81 mdfld_dsi_brightness_control(dev, 0, 82 dev_priv->brightness_adjusted); 83 } 84 85 if (dev_priv->dpi_panel_on[2]) 86 mdfld_dsi_brightness_control(dev, 2, 87 dev_priv->brightness_adjusted); 88 gma_power_end(dev); 89 } 90 91 /* cache the brightness for later use */ 92 dev_priv->brightness = level; 93 return 0; 94} 95 96static int mdfld_get_brightness(struct backlight_device *bd) 97{ 98 struct drm_device *dev = 99 (struct drm_device *)bl_get_data(mdfld_backlight_device); 100 struct drm_psb_private *dev_priv = dev->dev_private; 101 102 DRM_DEBUG_DRIVER("brightness = 0x%x \n", dev_priv->brightness); 103 104 /* return locally cached var instead of HW read (due to DPST etc.) */ 105 return dev_priv->brightness; 106} 107 108static const struct backlight_ops mdfld_ops = { 109 .get_brightness = mdfld_get_brightness, 110 .update_status = mdfld_set_brightness, 111}; 112 113static int device_backlight_init(struct drm_device *dev) 114{ 115 struct drm_psb_private *dev_priv = (struct drm_psb_private *) 116 dev->dev_private; 117 118 dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; 119 dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; 120 121 return 0; 122} 123 124static int mdfld_backlight_init(struct drm_device *dev) 125{ 126 struct backlight_properties props; 127 int ret = 0; 128 129 memset(&props, 0, sizeof(struct backlight_properties)); 130 props.max_brightness = BRIGHTNESS_MAX_LEVEL; 131 props.type = BACKLIGHT_PLATFORM; 132 mdfld_backlight_device = backlight_device_register("mdfld-bl", 133 NULL, (void *)dev, &mdfld_ops, &props); 134 135 if (IS_ERR(mdfld_backlight_device)) 136 return PTR_ERR(mdfld_backlight_device); 137 138 ret = device_backlight_init(dev); 139 if (ret) 140 return ret; 141 142 mdfld_backlight_device->props.brightness = BRIGHTNESS_MAX_LEVEL; 143 mdfld_backlight_device->props.max_brightness = BRIGHTNESS_MAX_LEVEL; 144 backlight_update_status(mdfld_backlight_device); 145 return 0; 146} 147#endif 148 149struct backlight_device *mdfld_get_backlight_device(void) 150{ 151#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE 152 return mdfld_backlight_device; 153#else 154 return NULL; 155#endif 156} 157 158/* 159 * mdfld_save_display_registers 160 * 161 * Description: We are going to suspend so save current display 162 * register state. 163 * 164 * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio 165 */ 166static int mdfld_save_display_registers(struct drm_device *dev, int pipenum) 167{ 168 struct drm_psb_private *dev_priv = dev->dev_private; 169 struct medfield_state *regs = &dev_priv->regs.mdfld; 170 struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum]; 171 const struct psb_offset *map = &dev_priv->regmap[pipenum]; 172 int i; 173 u32 *mipi_val; 174 175 /* register */ 176 u32 mipi_reg = MIPI; 177 178 switch (pipenum) { 179 case 0: 180 mipi_val = ®s->saveMIPI; 181 break; 182 case 1: 183 mipi_val = ®s->saveMIPI; 184 break; 185 case 2: 186 /* register */ 187 mipi_reg = MIPI_C; 188 /* pointer to values */ 189 mipi_val = ®s->saveMIPI_C; 190 break; 191 default: 192 DRM_ERROR("%s, invalid pipe number.\n", __func__); 193 return -EINVAL; 194 } 195 196 /* Pipe & plane A info */ 197 pipe->dpll = PSB_RVDC32(map->dpll); 198 pipe->fp0 = PSB_RVDC32(map->fp0); 199 pipe->conf = PSB_RVDC32(map->conf); 200 pipe->htotal = PSB_RVDC32(map->htotal); 201 pipe->hblank = PSB_RVDC32(map->hblank); 202 pipe->hsync = PSB_RVDC32(map->hsync); 203 pipe->vtotal = PSB_RVDC32(map->vtotal); 204 pipe->vblank = PSB_RVDC32(map->vblank); 205 pipe->vsync = PSB_RVDC32(map->vsync); 206 pipe->src = PSB_RVDC32(map->src); 207 pipe->stride = PSB_RVDC32(map->stride); 208 pipe->linoff = PSB_RVDC32(map->linoff); 209 pipe->tileoff = PSB_RVDC32(map->tileoff); 210 pipe->size = PSB_RVDC32(map->size); 211 pipe->pos = PSB_RVDC32(map->pos); 212 pipe->surf = PSB_RVDC32(map->surf); 213 pipe->cntr = PSB_RVDC32(map->cntr); 214 pipe->status = PSB_RVDC32(map->status); 215 216 /*save palette (gamma) */ 217 for (i = 0; i < 256; i++) 218 pipe->palette[i] = PSB_RVDC32(map->palette + (i << 2)); 219 220 if (pipenum == 1) { 221 regs->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); 222 regs->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); 223 224 regs->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL); 225 regs->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL); 226 return 0; 227 } 228 229 *mipi_val = PSB_RVDC32(mipi_reg); 230 return 0; 231} 232 233/* 234 * mdfld_restore_display_registers 235 * 236 * Description: We are going to resume so restore display register state. 237 * 238 * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio 239 */ 240static int mdfld_restore_display_registers(struct drm_device *dev, int pipenum) 241{ 242 /* To get panel out of ULPS mode. */ 243 u32 temp = 0; 244 u32 device_ready_reg = DEVICE_READY_REG; 245 struct drm_psb_private *dev_priv = dev->dev_private; 246 struct mdfld_dsi_config *dsi_config = NULL; 247 struct medfield_state *regs = &dev_priv->regs.mdfld; 248 struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum]; 249 const struct psb_offset *map = &dev_priv->regmap[pipenum]; 250 u32 i; 251 u32 dpll; 252 u32 timeout = 0; 253 254 /* register */ 255 u32 mipi_reg = MIPI; 256 257 /* values */ 258 u32 dpll_val = pipe->dpll; 259 u32 mipi_val = regs->saveMIPI; 260 261 switch (pipenum) { 262 case 0: 263 dpll_val &= ~DPLL_VCO_ENABLE; 264 dsi_config = dev_priv->dsi_configs[0]; 265 break; 266 case 1: 267 dpll_val &= ~DPLL_VCO_ENABLE; 268 break; 269 case 2: 270 mipi_reg = MIPI_C; 271 mipi_val = regs->saveMIPI_C; 272 dsi_config = dev_priv->dsi_configs[1]; 273 break; 274 default: 275 DRM_ERROR("%s, invalid pipe number.\n", __func__); 276 return -EINVAL; 277 } 278 279 /*make sure VGA plane is off. it initializes to on after reset!*/ 280 PSB_WVDC32(0x80000000, VGACNTRL); 281 282 if (pipenum == 1) { 283 PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, map->dpll); 284 PSB_RVDC32(map->dpll); 285 286 PSB_WVDC32(pipe->fp0, map->fp0); 287 } else { 288 289 dpll = PSB_RVDC32(map->dpll); 290 291 if (!(dpll & DPLL_VCO_ENABLE)) { 292 293 /* When ungating power of DPLL, needs to wait 0.5us 294 before enable the VCO */ 295 if (dpll & MDFLD_PWR_GATE_EN) { 296 dpll &= ~MDFLD_PWR_GATE_EN; 297 PSB_WVDC32(dpll, map->dpll); 298 /* FIXME_MDFLD PO - change 500 to 1 after PO */ 299 udelay(500); 300 } 301 302 PSB_WVDC32(pipe->fp0, map->fp0); 303 PSB_WVDC32(dpll_val, map->dpll); 304 /* FIXME_MDFLD PO - change 500 to 1 after PO */ 305 udelay(500); 306 307 dpll_val |= DPLL_VCO_ENABLE; 308 PSB_WVDC32(dpll_val, map->dpll); 309 PSB_RVDC32(map->dpll); 310 311 /* wait for DSI PLL to lock */ 312 while (timeout < 20000 && 313 !(PSB_RVDC32(map->conf) & PIPECONF_DSIPLL_LOCK)) { 314 udelay(150); 315 timeout++; 316 } 317 318 if (timeout == 20000) { 319 DRM_ERROR("%s, can't lock DSIPLL.\n", 320 __func__); 321 return -EINVAL; 322 } 323 } 324 } 325 /* Restore mode */ 326 PSB_WVDC32(pipe->htotal, map->htotal); 327 PSB_WVDC32(pipe->hblank, map->hblank); 328 PSB_WVDC32(pipe->hsync, map->hsync); 329 PSB_WVDC32(pipe->vtotal, map->vtotal); 330 PSB_WVDC32(pipe->vblank, map->vblank); 331 PSB_WVDC32(pipe->vsync, map->vsync); 332 PSB_WVDC32(pipe->src, map->src); 333 PSB_WVDC32(pipe->status, map->status); 334 335 /*set up the plane*/ 336 PSB_WVDC32(pipe->stride, map->stride); 337 PSB_WVDC32(pipe->linoff, map->linoff); 338 PSB_WVDC32(pipe->tileoff, map->tileoff); 339 PSB_WVDC32(pipe->size, map->size); 340 PSB_WVDC32(pipe->pos, map->pos); 341 PSB_WVDC32(pipe->surf, map->surf); 342 343 if (pipenum == 1) { 344 /* restore palette (gamma) */ 345 /*DRM_UDELAY(50000); */ 346 for (i = 0; i < 256; i++) 347 PSB_WVDC32(pipe->palette[i], map->palette + (i << 2)); 348 349 PSB_WVDC32(regs->savePFIT_CONTROL, PFIT_CONTROL); 350 PSB_WVDC32(regs->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); 351 352 /*TODO: resume HDMI port */ 353 354 /*TODO: resume pipe*/ 355 356 /*enable the plane*/ 357 PSB_WVDC32(pipe->cntr & ~DISPLAY_PLANE_ENABLE, map->cntr); 358 359 return 0; 360 } 361 362 /*set up pipe related registers*/ 363 PSB_WVDC32(mipi_val, mipi_reg); 364 365 /*setup MIPI adapter + MIPI IP registers*/ 366 if (dsi_config) 367 mdfld_dsi_controller_init(dsi_config, pipenum); 368 369 if (in_atomic() || in_interrupt()) 370 mdelay(20); 371 else 372 msleep(20); 373 374 /*enable the plane*/ 375 PSB_WVDC32(pipe->cntr, map->cntr); 376 377 if (in_atomic() || in_interrupt()) 378 mdelay(20); 379 else 380 msleep(20); 381 382 /* LP Hold Release */ 383 temp = REG_READ(mipi_reg); 384 temp |= LP_OUTPUT_HOLD_RELEASE; 385 REG_WRITE(mipi_reg, temp); 386 mdelay(1); 387 388 389 /* Set DSI host to exit from Utra Low Power State */ 390 temp = REG_READ(device_ready_reg); 391 temp &= ~ULPS_MASK; 392 temp |= 0x3; 393 temp |= EXIT_ULPS_DEV_READY; 394 REG_WRITE(device_ready_reg, temp); 395 mdelay(1); 396 397 temp = REG_READ(device_ready_reg); 398 temp &= ~ULPS_MASK; 399 temp |= EXITING_ULPS; 400 REG_WRITE(device_ready_reg, temp); 401 mdelay(1); 402 403 /*enable the pipe*/ 404 PSB_WVDC32(pipe->conf, map->conf); 405 406 /* restore palette (gamma) */ 407 /*DRM_UDELAY(50000); */ 408 for (i = 0; i < 256; i++) 409 PSB_WVDC32(pipe->palette[i], map->palette + (i << 2)); 410 411 return 0; 412} 413 414static int mdfld_save_registers(struct drm_device *dev) 415{ 416 /* mdfld_save_cursor_overlay_registers(dev); */ 417 mdfld_save_display_registers(dev, 0); 418 mdfld_save_display_registers(dev, 2); 419 mdfld_disable_crtc(dev, 0); 420 mdfld_disable_crtc(dev, 2); 421 422 return 0; 423} 424 425static int mdfld_restore_registers(struct drm_device *dev) 426{ 427 mdfld_restore_display_registers(dev, 2); 428 mdfld_restore_display_registers(dev, 0); 429 /* mdfld_restore_cursor_overlay_registers(dev); */ 430 431 return 0; 432} 433 434static int mdfld_power_down(struct drm_device *dev) 435{ 436 /* FIXME */ 437 return 0; 438} 439 440static int mdfld_power_up(struct drm_device *dev) 441{ 442 /* FIXME */ 443 return 0; 444} 445 446/* Medfield */ 447static const struct psb_offset mdfld_regmap[3] = { 448 { 449 .fp0 = MRST_FPA0, 450 .fp1 = MRST_FPA1, 451 .cntr = DSPACNTR, 452 .conf = PIPEACONF, 453 .src = PIPEASRC, 454 .dpll = MRST_DPLL_A, 455 .htotal = HTOTAL_A, 456 .hblank = HBLANK_A, 457 .hsync = HSYNC_A, 458 .vtotal = VTOTAL_A, 459 .vblank = VBLANK_A, 460 .vsync = VSYNC_A, 461 .stride = DSPASTRIDE, 462 .size = DSPASIZE, 463 .pos = DSPAPOS, 464 .surf = DSPASURF, 465 .addr = MRST_DSPABASE, 466 .status = PIPEASTAT, 467 .linoff = DSPALINOFF, 468 .tileoff = DSPATILEOFF, 469 .palette = PALETTE_A, 470 }, 471 { 472 .fp0 = MDFLD_DPLL_DIV0, 473 .cntr = DSPBCNTR, 474 .conf = PIPEBCONF, 475 .src = PIPEBSRC, 476 .dpll = MDFLD_DPLL_B, 477 .htotal = HTOTAL_B, 478 .hblank = HBLANK_B, 479 .hsync = HSYNC_B, 480 .vtotal = VTOTAL_B, 481 .vblank = VBLANK_B, 482 .vsync = VSYNC_B, 483 .stride = DSPBSTRIDE, 484 .size = DSPBSIZE, 485 .pos = DSPBPOS, 486 .surf = DSPBSURF, 487 .addr = MRST_DSPBBASE, 488 .status = PIPEBSTAT, 489 .linoff = DSPBLINOFF, 490 .tileoff = DSPBTILEOFF, 491 .palette = PALETTE_B, 492 }, 493 { 494 .fp0 = MRST_FPA0, /* This is what the old code did ?? */ 495 .cntr = DSPCCNTR, 496 .conf = PIPECCONF, 497 .src = PIPECSRC, 498 /* No DPLL_C */ 499 .dpll = MRST_DPLL_A, 500 .htotal = HTOTAL_C, 501 .hblank = HBLANK_C, 502 .hsync = HSYNC_C, 503 .vtotal = VTOTAL_C, 504 .vblank = VBLANK_C, 505 .vsync = VSYNC_C, 506 .stride = DSPCSTRIDE, 507 .size = DSPBSIZE, 508 .pos = DSPCPOS, 509 .surf = DSPCSURF, 510 .addr = MDFLD_DSPCBASE, 511 .status = PIPECSTAT, 512 .linoff = DSPCLINOFF, 513 .tileoff = DSPCTILEOFF, 514 .palette = PALETTE_C, 515 }, 516}; 517 518static int mdfld_chip_setup(struct drm_device *dev) 519{ 520 struct drm_psb_private *dev_priv = dev->dev_private; 521 if (pci_enable_msi(dev->pdev)) 522 dev_warn(dev->dev, "Enabling MSI failed!\n"); 523 dev_priv->regmap = mdfld_regmap; 524 return mid_chip_setup(dev); 525} 526 527const struct psb_ops mdfld_chip_ops = { 528 .name = "mdfld", 529 .accel_2d = 0, 530 .pipes = 3, 531 .crtcs = 3, 532 .lvds_mask = (1 << 1), 533 .hdmi_mask = (1 << 1), 534 .cursor_needs_phys = 0, 535 .sgx_offset = MRST_SGX_OFFSET, 536 537 .chip_setup = mdfld_chip_setup, 538 .crtc_helper = &mdfld_helper_funcs, 539 .crtc_funcs = &psb_intel_crtc_funcs, 540 541 .output_init = mdfld_output_init, 542 543#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE 544 .backlight_init = mdfld_backlight_init, 545#endif 546 547 .save_regs = mdfld_save_registers, 548 .restore_regs = mdfld_restore_registers, 549 .power_down = mdfld_power_down, 550 .power_up = mdfld_power_up, 551}; 552