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