1/* 2 * Copyright © 2010 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: 24 * jim liu <jim.liu@intel.com> 25 * Jackie Li<yaodong.li@intel.com> 26 */ 27 28#include <linux/delay.h> 29#include <linux/moduleparam.h> 30#include <linux/pm_runtime.h> 31#include <linux/gpio/consumer.h> 32 33#include <asm/intel_scu_ipc.h> 34 35#include "mdfld_dsi_dpi.h" 36#include "mdfld_dsi_output.h" 37#include "mdfld_dsi_pkg_sender.h" 38#include "mdfld_output.h" 39#include "tc35876x-dsi-lvds.h" 40 41/* get the LABC from command line. */ 42static int LABC_control = 1; 43 44#ifdef MODULE 45module_param(LABC_control, int, 0644); 46#else 47 48static int __init parse_LABC_control(char *arg) 49{ 50 /* LABC control can be passed in as a cmdline parameter */ 51 /* to enable this feature add LABC=1 to cmdline */ 52 /* to disable this feature add LABC=0 to cmdline */ 53 if (!arg) 54 return -EINVAL; 55 56 if (!strcasecmp(arg, "0")) 57 LABC_control = 0; 58 else if (!strcasecmp(arg, "1")) 59 LABC_control = 1; 60 61 return 0; 62} 63early_param("LABC", parse_LABC_control); 64#endif 65 66/** 67 * Check and see if the generic control or data buffer is empty and ready. 68 */ 69void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg, 70 u32 fifo_stat) 71{ 72 u32 GEN_BF_time_out_count; 73 74 /* Check MIPI Adatper command registers */ 75 for (GEN_BF_time_out_count = 0; 76 GEN_BF_time_out_count < GEN_FB_TIME_OUT; 77 GEN_BF_time_out_count++) { 78 if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat) 79 break; 80 udelay(100); 81 } 82 83 if (GEN_BF_time_out_count == GEN_FB_TIME_OUT) 84 DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n", 85 gen_fifo_stat_reg); 86} 87 88/** 89 * Manage the DSI MIPI keyboard and display brightness. 90 * FIXME: this is exported to OSPM code. should work out an specific 91 * display interface to OSPM. 92 */ 93 94void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe) 95{ 96 struct mdfld_dsi_pkg_sender *sender = 97 mdfld_dsi_get_pkg_sender(dsi_config); 98 struct drm_device *dev; 99 struct drm_psb_private *dev_priv; 100 u32 gen_ctrl_val; 101 102 if (!sender) { 103 DRM_ERROR("No sender found\n"); 104 return; 105 } 106 107 dev = sender->dev; 108 dev_priv = dev->dev_private; 109 110 /* Set default display backlight value to 85% (0xd8)*/ 111 mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1, 112 true); 113 114 /* Set minimum brightness setting of CABC function to 20% (0x33)*/ 115 mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true); 116 117 /* Enable backlight or/and LABC */ 118 gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON | 119 BACKLIGHT_ON; 120 if (LABC_control == 1) 121 gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO 122 | GAMMA_AUTO; 123 124 if (LABC_control == 1) 125 gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON; 126 127 dev_priv->mipi_ctrl_display = gen_ctrl_val; 128 129 mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val, 130 1, true); 131 132 mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true); 133} 134 135void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level) 136{ 137 struct mdfld_dsi_pkg_sender *sender; 138 struct drm_psb_private *dev_priv; 139 struct mdfld_dsi_config *dsi_config; 140 u32 gen_ctrl_val = 0; 141 int p_type = TMD_VID; 142 143 if (!dev || (pipe != 0 && pipe != 2)) { 144 DRM_ERROR("Invalid parameter\n"); 145 return; 146 } 147 148 p_type = mdfld_get_panel_type(dev, 0); 149 150 dev_priv = dev->dev_private; 151 152 if (pipe) 153 dsi_config = dev_priv->dsi_configs[1]; 154 else 155 dsi_config = dev_priv->dsi_configs[0]; 156 157 sender = mdfld_dsi_get_pkg_sender(dsi_config); 158 159 if (!sender) { 160 DRM_ERROR("No sender found\n"); 161 return; 162 } 163 164 gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff; 165 166 dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n", 167 pipe, gen_ctrl_val); 168 169 if (p_type == TMD_VID) { 170 /* Set display backlight value */ 171 mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness, 172 (u8)gen_ctrl_val, 1, true); 173 } else { 174 /* Set display backlight value */ 175 mdfld_dsi_send_mcs_short(sender, write_display_brightness, 176 (u8)gen_ctrl_val, 1, true); 177 178 /* Enable backlight control */ 179 if (level == 0) 180 gen_ctrl_val = 0; 181 else 182 gen_ctrl_val = dev_priv->mipi_ctrl_display; 183 184 mdfld_dsi_send_mcs_short(sender, write_ctrl_display, 185 (u8)gen_ctrl_val, 1, true); 186 } 187} 188 189static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config, 190 u8 dcs, u32 *data, bool hs) 191{ 192 struct mdfld_dsi_pkg_sender *sender 193 = mdfld_dsi_get_pkg_sender(dsi_config); 194 195 if (!sender || !data) { 196 DRM_ERROR("Invalid parameter\n"); 197 return -EINVAL; 198 } 199 200 return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs); 201} 202 203int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode, 204 bool hs) 205{ 206 if (!dsi_config || !mode) { 207 DRM_ERROR("Invalid parameter\n"); 208 return -EINVAL; 209 } 210 211 return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs); 212} 213 214/* 215 * NOTE: this function was used by OSPM. 216 * TODO: will be removed later, should work out display interfaces for OSPM 217 */ 218void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe) 219{ 220 if (!dsi_config || ((pipe != 0) && (pipe != 2))) { 221 DRM_ERROR("Invalid parameters\n"); 222 return; 223 } 224 225 mdfld_dsi_dpi_controller_init(dsi_config, pipe); 226} 227 228static void mdfld_dsi_connector_save(struct drm_connector *connector) 229{ 230} 231 232static void mdfld_dsi_connector_restore(struct drm_connector *connector) 233{ 234} 235 236/* FIXME: start using the force parameter */ 237static enum drm_connector_status 238mdfld_dsi_connector_detect(struct drm_connector *connector, bool force) 239{ 240 struct mdfld_dsi_connector *dsi_connector 241 = mdfld_dsi_connector(connector); 242 243 dsi_connector->status = connector_status_connected; 244 245 return dsi_connector->status; 246} 247 248static int mdfld_dsi_connector_set_property(struct drm_connector *connector, 249 struct drm_property *property, 250 uint64_t value) 251{ 252 struct drm_encoder *encoder = connector->encoder; 253 254 if (!strcmp(property->name, "scaling mode") && encoder) { 255 struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc); 256 bool centerechange; 257 uint64_t val; 258 259 if (!gma_crtc) 260 goto set_prop_error; 261 262 switch (value) { 263 case DRM_MODE_SCALE_FULLSCREEN: 264 break; 265 case DRM_MODE_SCALE_NO_SCALE: 266 break; 267 case DRM_MODE_SCALE_ASPECT: 268 break; 269 default: 270 goto set_prop_error; 271 } 272 273 if (drm_object_property_get_value(&connector->base, property, &val)) 274 goto set_prop_error; 275 276 if (val == value) 277 goto set_prop_done; 278 279 if (drm_object_property_set_value(&connector->base, 280 property, value)) 281 goto set_prop_error; 282 283 centerechange = (val == DRM_MODE_SCALE_NO_SCALE) || 284 (value == DRM_MODE_SCALE_NO_SCALE); 285 286 if (gma_crtc->saved_mode.hdisplay != 0 && 287 gma_crtc->saved_mode.vdisplay != 0) { 288 if (centerechange) { 289 if (!drm_crtc_helper_set_mode(encoder->crtc, 290 &gma_crtc->saved_mode, 291 encoder->crtc->x, 292 encoder->crtc->y, 293 encoder->crtc->primary->fb)) 294 goto set_prop_error; 295 } else { 296 const struct drm_encoder_helper_funcs *funcs = 297 encoder->helper_private; 298 funcs->mode_set(encoder, 299 &gma_crtc->saved_mode, 300 &gma_crtc->saved_adjusted_mode); 301 } 302 } 303 } else if (!strcmp(property->name, "backlight") && encoder) { 304 if (drm_object_property_set_value(&connector->base, property, 305 value)) 306 goto set_prop_error; 307 else 308 gma_backlight_set(encoder->dev, value); 309 } 310set_prop_done: 311 return 0; 312set_prop_error: 313 return -1; 314} 315 316static void mdfld_dsi_connector_destroy(struct drm_connector *connector) 317{ 318 struct mdfld_dsi_connector *dsi_connector = 319 mdfld_dsi_connector(connector); 320 struct mdfld_dsi_pkg_sender *sender; 321 322 if (!dsi_connector) 323 return; 324 drm_connector_unregister(connector); 325 drm_connector_cleanup(connector); 326 sender = dsi_connector->pkg_sender; 327 mdfld_dsi_pkg_sender_destroy(sender); 328 kfree(dsi_connector); 329} 330 331static int mdfld_dsi_connector_get_modes(struct drm_connector *connector) 332{ 333 struct mdfld_dsi_connector *dsi_connector = 334 mdfld_dsi_connector(connector); 335 struct mdfld_dsi_config *dsi_config = 336 mdfld_dsi_get_config(dsi_connector); 337 struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; 338 struct drm_display_mode *dup_mode = NULL; 339 struct drm_device *dev = connector->dev; 340 341 if (fixed_mode) { 342 dev_dbg(dev->dev, "fixed_mode %dx%d\n", 343 fixed_mode->hdisplay, fixed_mode->vdisplay); 344 dup_mode = drm_mode_duplicate(dev, fixed_mode); 345 drm_mode_probed_add(connector, dup_mode); 346 return 1; 347 } 348 DRM_ERROR("Didn't get any modes!\n"); 349 return 0; 350} 351 352static enum drm_mode_status mdfld_dsi_connector_mode_valid(struct drm_connector *connector, 353 struct drm_display_mode *mode) 354{ 355 struct mdfld_dsi_connector *dsi_connector = 356 mdfld_dsi_connector(connector); 357 struct mdfld_dsi_config *dsi_config = 358 mdfld_dsi_get_config(dsi_connector); 359 struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; 360 361 if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 362 return MODE_NO_DBLESCAN; 363 364 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 365 return MODE_NO_INTERLACE; 366 367 /** 368 * FIXME: current DC has no fitting unit, reject any mode setting 369 * request 370 * Will figure out a way to do up-scaling(panel fitting) later. 371 **/ 372 if (fixed_mode) { 373 if (mode->hdisplay != fixed_mode->hdisplay) 374 return MODE_PANEL; 375 376 if (mode->vdisplay != fixed_mode->vdisplay) 377 return MODE_PANEL; 378 } 379 380 return MODE_OK; 381} 382 383static struct drm_encoder *mdfld_dsi_connector_best_encoder( 384 struct drm_connector *connector) 385{ 386 struct mdfld_dsi_connector *dsi_connector = 387 mdfld_dsi_connector(connector); 388 struct mdfld_dsi_config *dsi_config = 389 mdfld_dsi_get_config(dsi_connector); 390 return &dsi_config->encoder->base.base; 391} 392 393/*DSI connector funcs*/ 394static const struct drm_connector_funcs mdfld_dsi_connector_funcs = { 395 .dpms = drm_helper_connector_dpms, 396 .detect = mdfld_dsi_connector_detect, 397 .fill_modes = drm_helper_probe_single_connector_modes, 398 .set_property = mdfld_dsi_connector_set_property, 399 .destroy = mdfld_dsi_connector_destroy, 400}; 401 402/*DSI connector helper funcs*/ 403static const struct drm_connector_helper_funcs 404 mdfld_dsi_connector_helper_funcs = { 405 .get_modes = mdfld_dsi_connector_get_modes, 406 .mode_valid = mdfld_dsi_connector_mode_valid, 407 .best_encoder = mdfld_dsi_connector_best_encoder, 408}; 409 410static int mdfld_dsi_get_default_config(struct drm_device *dev, 411 struct mdfld_dsi_config *config, int pipe) 412{ 413 if (!dev || !config) { 414 DRM_ERROR("Invalid parameters"); 415 return -EINVAL; 416 } 417 418 config->bpp = 24; 419 if (mdfld_get_panel_type(dev, pipe) == TC35876X) 420 config->lane_count = 4; 421 else 422 config->lane_count = 2; 423 config->channel_num = 0; 424 425 if (mdfld_get_panel_type(dev, pipe) == TMD_VID) 426 config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE; 427 else if (mdfld_get_panel_type(dev, pipe) == TC35876X) 428 config->video_mode = 429 MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS; 430 else 431 config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE; 432 433 return 0; 434} 435 436int mdfld_dsi_panel_reset(struct drm_device *ddev, int pipe) 437{ 438 struct device *dev = ddev->dev; 439 struct gpio_desc *gpiod; 440 441 /* 442 * Raise the GPIO reset line for the corresponding pipe to HIGH, 443 * this is probably because it is active low so this takes the 444 * respective pipe out of reset. (We have no code to put it back 445 * into reset in this driver.) 446 */ 447 switch (pipe) { 448 case 0: 449 gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_OUT_HIGH); 450 if (IS_ERR(gpiod)) 451 return PTR_ERR(gpiod); 452 break; 453 case 2: 454 gpiod = gpiod_get(dev, "dsi-pipe2-reset", GPIOD_OUT_HIGH); 455 if (IS_ERR(gpiod)) 456 return PTR_ERR(gpiod); 457 break; 458 default: 459 DRM_DEV_ERROR(dev, "Invalid output pipe\n"); 460 return -EINVAL; 461 } 462 gpiod_put(gpiod); 463 464 /* Flush posted writes on the device */ 465 gpiod = gpiod_get(dev, "dsi-pipe0-reset", GPIOD_ASIS); 466 if (IS_ERR(gpiod)) 467 return PTR_ERR(gpiod); 468 gpiod_get_value(gpiod); 469 gpiod_put(gpiod); 470 471 return 0; 472} 473 474/* 475 * MIPI output init 476 * @dev drm device 477 * @pipe pipe number. 0 or 2 478 * @config 479 * 480 * Do the initialization of a MIPI output, including create DRM mode objects 481 * initialization of DSI output on @pipe 482 */ 483void mdfld_dsi_output_init(struct drm_device *dev, 484 int pipe, 485 const struct panel_funcs *p_vid_funcs) 486{ 487 struct mdfld_dsi_config *dsi_config; 488 struct mdfld_dsi_connector *dsi_connector; 489 struct drm_connector *connector; 490 struct mdfld_dsi_encoder *encoder; 491 struct drm_psb_private *dev_priv = dev->dev_private; 492 struct panel_info dsi_panel_info; 493 u32 width_mm, height_mm; 494 495 dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe); 496 497 if (pipe != 0 && pipe != 2) { 498 DRM_ERROR("Invalid parameter\n"); 499 return; 500 } 501 502 /*create a new connector*/ 503 dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL); 504 if (!dsi_connector) { 505 DRM_ERROR("No memory"); 506 return; 507 } 508 509 dsi_connector->pipe = pipe; 510 511 dsi_config = kzalloc(sizeof(struct mdfld_dsi_config), 512 GFP_KERNEL); 513 if (!dsi_config) { 514 DRM_ERROR("cannot allocate memory for DSI config\n"); 515 goto dsi_init_err0; 516 } 517 mdfld_dsi_get_default_config(dev, dsi_config, pipe); 518 519 dsi_connector->private = dsi_config; 520 521 dsi_config->changed = 1; 522 dsi_config->dev = dev; 523 524 dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev); 525 if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) 526 goto dsi_init_err0; 527 528 width_mm = dsi_panel_info.width_mm; 529 height_mm = dsi_panel_info.height_mm; 530 531 dsi_config->mode = dsi_config->fixed_mode; 532 dsi_config->connector = dsi_connector; 533 534 if (!dsi_config->fixed_mode) { 535 DRM_ERROR("No panel fixed mode was found\n"); 536 goto dsi_init_err0; 537 } 538 539 if (pipe && dev_priv->dsi_configs[0]) { 540 dsi_config->dvr_ic_inited = 0; 541 dev_priv->dsi_configs[1] = dsi_config; 542 } else if (pipe == 0) { 543 dsi_config->dvr_ic_inited = 1; 544 dev_priv->dsi_configs[0] = dsi_config; 545 } else { 546 DRM_ERROR("Trying to init MIPI1 before MIPI0\n"); 547 goto dsi_init_err0; 548 } 549 550 551 connector = &dsi_connector->base.base; 552 dsi_connector->base.save = mdfld_dsi_connector_save; 553 dsi_connector->base.restore = mdfld_dsi_connector_restore; 554 555 drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs, 556 DRM_MODE_CONNECTOR_LVDS); 557 drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs); 558 559 connector->display_info.subpixel_order = SubPixelHorizontalRGB; 560 connector->display_info.width_mm = width_mm; 561 connector->display_info.height_mm = height_mm; 562 connector->interlace_allowed = false; 563 connector->doublescan_allowed = false; 564 565 /*attach properties*/ 566 drm_object_attach_property(&connector->base, 567 dev->mode_config.scaling_mode_property, 568 DRM_MODE_SCALE_FULLSCREEN); 569 drm_object_attach_property(&connector->base, 570 dev_priv->backlight_property, 571 MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); 572 573 /*init DSI package sender on this output*/ 574 if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) { 575 DRM_ERROR("Package Sender initialization failed on pipe %d\n", 576 pipe); 577 goto dsi_init_err0; 578 } 579 580 encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs); 581 if (!encoder) { 582 DRM_ERROR("Create DPI encoder failed\n"); 583 goto dsi_init_err1; 584 } 585 encoder->private = dsi_config; 586 dsi_config->encoder = encoder; 587 encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI : 588 INTEL_OUTPUT_MIPI2; 589 drm_connector_register(connector); 590 return; 591 592 /*TODO: add code to destroy outputs on error*/ 593dsi_init_err1: 594 /*destroy sender*/ 595 mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender); 596 597 drm_connector_cleanup(connector); 598 599 kfree(dsi_config->fixed_mode); 600 kfree(dsi_config); 601dsi_init_err0: 602 kfree(dsi_connector); 603} 604