1/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 and 5 * only version 2 as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 15 * 02110-1301, USA. 16 */ 17 18#include <linux/module.h> 19#include <linux/kernel.h> 20#include <linux/sched.h> 21#include <linux/time.h> 22#include <linux/init.h> 23#include <linux/interrupt.h> 24#include <linux/hrtimer.h> 25 26#include <mach/hardware.h> 27#include <linux/io.h> 28 29#include <asm/system.h> 30#include <asm/mach-types.h> 31#include <linux/semaphore.h> 32#include <linux/spinlock.h> 33 34#include <linux/fb.h> 35 36#include "mdp.h" 37#include "msm_fb.h" 38#include "mddihost.h" 39 40static uint32 mdp_last_dma2_update_width; 41static uint32 mdp_last_dma2_update_height; 42static uint32 mdp_curr_dma2_update_width; 43static uint32 mdp_curr_dma2_update_height; 44 45ktime_t mdp_dma2_last_update_time = { 0 }; 46 47int mdp_lcd_rd_cnt_offset_slow = 20; 48int mdp_lcd_rd_cnt_offset_fast = 20; 49int mdp_vsync_usec_wait_line_too_short = 5; 50uint32 mdp_dma2_update_time_in_usec; 51uint32 mdp_total_vdopkts; 52 53extern u32 msm_fb_debug_enabled; 54extern struct workqueue_struct *mdp_dma_wq; 55 56int vsync_start_y_adjust = 4; 57 58static void mdp_dma2_update_lcd(struct msm_fb_data_type *mfd) 59{ 60 MDPIBUF *iBuf = &mfd->ibuf; 61 int mddi_dest = FALSE; 62 uint32 outBpp = iBuf->bpp; 63 uint32 dma2_cfg_reg; 64 uint8 *src; 65 uint32 mddi_ld_param; 66 uint16 mddi_vdo_packet_reg; 67 struct msm_fb_panel_data *pdata = 68 (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; 69 uint32 ystride = mfd->fbi->fix.line_length; 70 71 dma2_cfg_reg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | 72 DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; 73 74#ifdef CONFIG_FB_MSM_MDP30 75 if (iBuf->dma_w == 1) { 76 iBuf->dma_w = 2; 77 if (iBuf->dma_x == (iBuf->ibuf_width - 2)) 78 iBuf->dma_x--; 79 } 80#endif 81 82 if (mfd->fb_imgType == MDP_BGR_565) 83 dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; 84 else 85 dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; 86 87 if (outBpp == 4) 88 dma2_cfg_reg |= DMA_IBUF_C3ALPHA_EN; 89 90 if (outBpp == 2) 91 dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565; 92 93 mddi_ld_param = 0; 94 mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt; 95 96 if ((mfd->panel_info.type == MDDI_PANEL) || 97 (mfd->panel_info.type == EXT_MDDI_PANEL)) { 98 dma2_cfg_reg |= DMA_OUT_SEL_MDDI; 99 mddi_dest = TRUE; 100 101 if (mfd->panel_info.type == MDDI_PANEL) { 102 mdp_total_vdopkts++; 103 if (mfd->panel_info.pdest == DISPLAY_1) { 104 dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; 105 mddi_ld_param = 0; 106#ifdef MDDI_HOST_WINDOW_WORKAROUND 107 mddi_window_adjust(mfd, iBuf->dma_x, 108 iBuf->dma_w - 1, iBuf->dma_y, 109 iBuf->dma_h - 1); 110#endif 111 } else { 112 dma2_cfg_reg |= 113 DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY; 114 mddi_ld_param = 1; 115#ifdef MDDI_HOST_WINDOW_WORKAROUND 116 mddi_window_adjust(mfd, iBuf->dma_x, 117 iBuf->dma_w - 1, iBuf->dma_y, 118 iBuf->dma_h - 1); 119#endif 120 } 121 } else { 122 dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL; 123 mddi_ld_param = 2; 124 } 125 } else { 126 if (mfd->panel_info.pdest == DISPLAY_1) { 127 dma2_cfg_reg |= DMA_AHBM_LCD_SEL_PRIMARY; 128 outp32(MDP_EBI2_LCD0, mfd->data_port_phys); 129 } else { 130 dma2_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY; 131 outp32(MDP_EBI2_LCD1, mfd->data_port_phys); 132 } 133 } 134 135 dma2_cfg_reg |= DMA_DITHER_EN; 136 137 src = (uint8 *) iBuf->buf; 138 /* starting input address */ 139 src += iBuf->dma_x * outBpp + iBuf->dma_y * ystride; 140 141 mdp_curr_dma2_update_width = iBuf->dma_w; 142 mdp_curr_dma2_update_height = iBuf->dma_h; 143 144 /* MDP cmd block enable */ 145 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); 146 147#ifdef CONFIG_FB_MSM_MDP22 148 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0184, 149 (iBuf->dma_h << 16 | iBuf->dma_w)); 150 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0188, src); 151 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x018C, ystride); 152#else 153 MDP_OUTP(MDP_BASE + 0x90004, (iBuf->dma_h << 16 | iBuf->dma_w)); 154 MDP_OUTP(MDP_BASE + 0x90008, src); 155 MDP_OUTP(MDP_BASE + 0x9000c, ystride); 156#endif 157 158 if (mfd->panel_info.bpp == 18) { 159 dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */ 160 DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; 161 } else { 162 dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */ 163 DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; 164 } 165 166 if (mddi_dest) { 167#ifdef CONFIG_FB_MSM_MDP22 168 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0194, 169 (iBuf->dma_y << 16) | iBuf->dma_x); 170 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0, mddi_ld_param); 171 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4, 172 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); 173#else 174 MDP_OUTP(MDP_BASE + 0x90010, (iBuf->dma_y << 16) | iBuf->dma_x); 175 MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param); 176 MDP_OUTP(MDP_BASE + 0x00094, 177 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg); 178#endif 179 } else { 180 /* setting EBI2 LCDC write window */ 181 pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w, 182 iBuf->dma_h); 183 } 184 185 /* dma2 config register */ 186#ifdef MDP_HW_VSYNC 187 MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); 188 189 if ((mfd->use_mdp_vsync) && 190 (mfd->ibuf.vsync_enable) && (mfd->panel_info.lcd.vsync_enable)) { 191 uint32 start_y; 192 193 if (vsync_start_y_adjust <= iBuf->dma_y) 194 start_y = iBuf->dma_y - vsync_start_y_adjust; 195 else 196 start_y = 197 (mfd->total_lcd_lines - 1) - (vsync_start_y_adjust - 198 iBuf->dma_y); 199 200 /* 201 * MDP VSYNC clock must be On by now so, we don't have to 202 * re-enable it 203 */ 204 MDP_OUTP(MDP_BASE + 0x210, start_y); 205 MDP_OUTP(MDP_BASE + 0x20c, 1); /* enable prim vsync */ 206 } else { 207 MDP_OUTP(MDP_BASE + 0x20c, 0); /* disable prim vsync */ 208 } 209#else 210#ifdef CONFIG_FB_MSM_MDP22 211 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0180, dma2_cfg_reg); 212#else 213 MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg); 214#endif 215#endif /* MDP_HW_VSYNC */ 216 217 /* MDP cmd block disable */ 218 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); 219} 220 221static ktime_t vt = { 0 }; 222int mdp_usec_diff_threshold = 100; 223int mdp_expected_usec_wait; 224 225enum hrtimer_restart mdp_dma2_vsync_hrtimer_handler(struct hrtimer *ht) 226{ 227 struct msm_fb_data_type *mfd = NULL; 228 229 mfd = container_of(ht, struct msm_fb_data_type, dma_hrtimer); 230 231 mdp_pipe_kickoff(MDP_DMA2_TERM, mfd); 232 233 if (msm_fb_debug_enabled) { 234 ktime_t t; 235 int usec_diff; 236 int actual_wait; 237 238 t = ktime_get_real(); 239 240 actual_wait = 241 (t.tv.sec - vt.tv.sec) * 1000000 + (t.tv.nsec - 242 vt.tv.nsec) / 1000; 243 usec_diff = actual_wait - mdp_expected_usec_wait; 244 245 if ((mdp_usec_diff_threshold < usec_diff) || (usec_diff < 0)) 246 MSM_FB_DEBUG 247 ("HRT Diff = %d usec Exp=%d usec Act=%d usec\n", 248 usec_diff, mdp_expected_usec_wait, actual_wait); 249 } 250 251 return HRTIMER_NORESTART; 252} 253 254static void mdp_dma_schedule(struct msm_fb_data_type *mfd, uint32 term) 255{ 256 /* 257 * dma2 configure VSYNC block 258 * vsync supported on Primary LCD only for now 259 */ 260 int32 mdp_lcd_rd_cnt; 261 uint32 usec_wait_time; 262 uint32 start_y; 263 264 /* 265 * ToDo: if we can move HRT timer callback to workqueue, we can 266 * move DMA2 power on under mdp_pipe_kickoff(). 267 * This will save a power for hrt time wait. 268 * However if the latency for context switch (hrt irq -> workqueue) 269 * is too big, we will miss the vsync timing. 270 */ 271 if (term == MDP_DMA2_TERM) 272 mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE); 273 274 mdp_dma2_update_time_in_usec = 275 MDP_KTIME2USEC(mdp_dma2_last_update_time); 276 277 if ((!mfd->ibuf.vsync_enable) || (!mfd->panel_info.lcd.vsync_enable) 278 || (mfd->use_mdp_vsync)) { 279 mdp_pipe_kickoff(term, mfd); 280 return; 281 } 282 /* SW vsync logic starts here */ 283 284 /* get current rd counter */ 285 mdp_lcd_rd_cnt = mdp_get_lcd_line_counter(mfd); 286 if (mdp_dma2_update_time_in_usec != 0) { 287 uint32 num, den; 288 289 /* 290 * roi width boundary calculation to know the size of pixel 291 * width that MDP can send faster or slower than LCD read 292 * pointer 293 */ 294 295 num = mdp_last_dma2_update_width * mdp_last_dma2_update_height; 296 den = 297 (((mfd->panel_info.lcd.refx100 * mfd->total_lcd_lines) / 298 1000) * (mdp_dma2_update_time_in_usec / 100)) / 1000; 299 300 if (den == 0) 301 mfd->vsync_width_boundary[mdp_last_dma2_update_width] = 302 mfd->panel_info.xres + 1; 303 else 304 mfd->vsync_width_boundary[mdp_last_dma2_update_width] = 305 (int)(num / den); 306 } 307 308 if (mfd->vsync_width_boundary[mdp_last_dma2_update_width] > 309 mdp_curr_dma2_update_width) { 310 /* MDP wrp is faster than LCD rdp */ 311 mdp_lcd_rd_cnt += mdp_lcd_rd_cnt_offset_fast; 312 } else { 313 /* MDP wrp is slower than LCD rdp */ 314 mdp_lcd_rd_cnt -= mdp_lcd_rd_cnt_offset_slow; 315 } 316 317 if (mdp_lcd_rd_cnt < 0) 318 mdp_lcd_rd_cnt = mfd->total_lcd_lines + mdp_lcd_rd_cnt; 319 else if (mdp_lcd_rd_cnt > mfd->total_lcd_lines) 320 mdp_lcd_rd_cnt = mdp_lcd_rd_cnt - mfd->total_lcd_lines - 1; 321 322 /* get wrt pointer position */ 323 start_y = mfd->ibuf.dma_y; 324 325 /* measure line difference between start_y and rd counter */ 326 if (start_y > mdp_lcd_rd_cnt) { 327 /* 328 * *100 for lcd_ref_hzx100 was already multiplied by 100 329 * *1000000 is for usec conversion 330 */ 331 332 if ((start_y - mdp_lcd_rd_cnt) <= 333 mdp_vsync_usec_wait_line_too_short) 334 usec_wait_time = 0; 335 else 336 usec_wait_time = 337 ((start_y - 338 mdp_lcd_rd_cnt) * 1000000) / 339 ((mfd->total_lcd_lines * 340 mfd->panel_info.lcd.refx100) / 100); 341 } else { 342 if ((start_y + (mfd->total_lcd_lines - mdp_lcd_rd_cnt)) <= 343 mdp_vsync_usec_wait_line_too_short) 344 usec_wait_time = 0; 345 else 346 usec_wait_time = 347 ((start_y + 348 (mfd->total_lcd_lines - 349 mdp_lcd_rd_cnt)) * 1000000) / 350 ((mfd->total_lcd_lines * 351 mfd->panel_info.lcd.refx100) / 100); 352 } 353 354 mdp_last_dma2_update_width = mdp_curr_dma2_update_width; 355 mdp_last_dma2_update_height = mdp_curr_dma2_update_height; 356 357 if (usec_wait_time == 0) { 358 mdp_pipe_kickoff(term, mfd); 359 } else { 360 ktime_t wait_time; 361 362 wait_time.tv.sec = 0; 363 wait_time.tv.nsec = usec_wait_time * 1000; 364 365 if (msm_fb_debug_enabled) { 366 vt = ktime_get_real(); 367 mdp_expected_usec_wait = usec_wait_time; 368 } 369 hrtimer_start(&mfd->dma_hrtimer, wait_time, HRTIMER_MODE_REL); 370 } 371} 372 373#ifdef MDDI_HOST_WINDOW_WORKAROUND 374void mdp_dma2_update(struct msm_fb_data_type *mfd) 375{ 376 MDPIBUF *iBuf; 377 uint32 upper_height; 378 379 if (mfd->panel.type == EXT_MDDI_PANEL) { 380 mdp_dma2_update_sub(mfd); 381 return; 382 } 383 384 iBuf = &mfd->ibuf; 385 386 upper_height = 387 (uint32) mddi_assign_pkt_height((uint16) iBuf->dma_w, 388 (uint16) iBuf->dma_h, 18); 389 390 if (upper_height >= iBuf->dma_h) { 391 mdp_dma2_update_sub(mfd); 392 } else { 393 MDPIBUF lower_height; 394 395 /* sending the upper region first */ 396 lower_height = iBuf->dma_h - upper_height; 397 iBuf->dma_h = upper_height; 398 mdp_dma2_update_sub(mfd); 399 400 /* sending the lower region second */ 401 iBuf->dma_h = lower_height; 402 iBuf->dma_y += lower_height; 403 iBuf->vsync_enable = FALSE; 404 mdp_dma2_update_sub(mfd); 405 } 406} 407 408void mdp_dma2_update_sub(struct msm_fb_data_type *mfd) 409#else 410void mdp_dma2_update(struct msm_fb_data_type *mfd) 411#endif 412{ 413 down(&mfd->dma->mutex); 414 if ((mfd) && (!mfd->dma->busy) && (mfd->panel_power_on)) { 415 down(&mfd->sem); 416 mfd->ibuf_flushed = TRUE; 417 mdp_dma2_update_lcd(mfd); 418 419 mdp_enable_irq(MDP_DMA2_TERM); 420 mfd->dma->busy = TRUE; 421 INIT_COMPLETION(mfd->dma->comp); 422 423 /* schedule DMA to start */ 424 mdp_dma_schedule(mfd, MDP_DMA2_TERM); 425 up(&mfd->sem); 426 427 /* wait until DMA finishes the current job */ 428 wait_for_completion_killable(&mfd->dma->comp); 429 mdp_disable_irq(MDP_DMA2_TERM); 430 431 /* signal if pan function is waiting for the update completion */ 432 if (mfd->pan_waiting) { 433 mfd->pan_waiting = FALSE; 434 complete(&mfd->pan_comp); 435 } 436 } 437 up(&mfd->dma->mutex); 438} 439 440void mdp_lcd_update_workqueue_handler(struct work_struct *work) 441{ 442 struct msm_fb_data_type *mfd = NULL; 443 444 mfd = container_of(work, struct msm_fb_data_type, dma_update_worker); 445 if (mfd) 446 mfd->dma_fnc(mfd); 447} 448 449void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty, 450 boolean sync) 451{ 452 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; 453 MDPIBUF *iBuf; 454 int bpp = info->var.bits_per_pixel / 8; 455 456 down(&mfd->sem); 457 iBuf = &mfd->ibuf; 458 iBuf->buf = (uint8 *) info->fix.smem_start; 459 iBuf->buf += info->var.xoffset * bpp + 460 info->var.yoffset * info->fix.line_length; 461 462 iBuf->ibuf_width = info->var.xres_virtual; 463 iBuf->bpp = bpp; 464 465 iBuf->vsync_enable = sync; 466 467 if (dirty) { 468 /* 469 * ToDo: dirty region check inside var.xoffset+xres 470 * <-> var.yoffset+yres 471 */ 472 iBuf->dma_x = dirty->xoffset % info->var.xres; 473 iBuf->dma_y = dirty->yoffset % info->var.yres; 474 iBuf->dma_w = dirty->width; 475 iBuf->dma_h = dirty->height; 476 } else { 477 iBuf->dma_x = 0; 478 iBuf->dma_y = 0; 479 iBuf->dma_w = info->var.xres; 480 iBuf->dma_h = info->var.yres; 481 } 482 mfd->ibuf_flushed = FALSE; 483 up(&mfd->sem); 484} 485 486void mdp_set_offset_info(struct fb_info *info, uint32 addr, uint32 sync) 487{ 488 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; 489 MDPIBUF *iBuf; 490 491 int bpp = info->var.bits_per_pixel / 8; 492 493 down(&mfd->sem); 494 iBuf = &mfd->ibuf; 495 iBuf->ibuf_width = info->var.xres_virtual; 496 iBuf->bpp = bpp; 497 iBuf->vsync_enable = sync; 498 iBuf->dma_x = 0; 499 iBuf->dma_y = 0; 500 iBuf->dma_w = info->var.xres; 501 iBuf->dma_h = info->var.yres; 502 iBuf->buf = (uint8 *) addr; 503 504 mfd->ibuf_flushed = FALSE; 505 up(&mfd->sem); 506} 507 508void mdp_dma_pan_update(struct fb_info *info) 509{ 510 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; 511 MDPIBUF *iBuf; 512 513 iBuf = &mfd->ibuf; 514 515 if (mfd->sw_currently_refreshing) { 516 /* we need to wait for the pending update */ 517 mfd->pan_waiting = TRUE; 518 if (!mfd->ibuf_flushed) { 519 wait_for_completion_killable(&mfd->pan_comp); 520 } 521 /* waiting for this update to complete */ 522 mfd->pan_waiting = TRUE; 523 wait_for_completion_killable(&mfd->pan_comp); 524 } else 525 mfd->dma_fnc(mfd); 526} 527 528void mdp_refresh_screen(unsigned long data) 529{ 530 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; 531 532 if ((mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) { 533 init_timer(&mfd->refresh_timer); 534 mfd->refresh_timer.function = mdp_refresh_screen; 535 mfd->refresh_timer.data = data; 536 537 if (mfd->dma->busy) 538 /* come back in 1 msec */ 539 mfd->refresh_timer.expires = jiffies + (HZ / 1000); 540 else 541 mfd->refresh_timer.expires = 542 jiffies + mfd->refresh_timer_duration; 543 544 add_timer(&mfd->refresh_timer); 545 546 if (!mfd->dma->busy) { 547 if (!queue_work(mdp_dma_wq, &mfd->dma_update_worker)) { 548 MSM_FB_DEBUG("mdp_dma: can't queue_work! -> \ 549 MDP/MDDI/LCD clock speed needs to be increased\n"); 550 } 551 } 552 } else { 553 if (!mfd->hw_refresh) 554 complete(&mfd->refresher_comp); 555 } 556} 557