1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
76
77
78
79
80 if (iBuf->dma_w == 1) {
81 iBuf->dma_w = 2;
82 if (iBuf->dma_x == (iBuf->ibuf_width - 2))
83 iBuf->dma_x--;
84 }
85#endif
86
87 if (mfd->fb_imgType == MDP_BGR_565)
88 dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
89 else
90 dma2_cfg_reg |= DMA_PACK_PATTERN_RGB;
91
92 if (outBpp == 4)
93 dma2_cfg_reg |= DMA_IBUF_C3ALPHA_EN;
94
95 if (outBpp == 2)
96 dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
97
98 mddi_ld_param = 0;
99 mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
100
101 if ((mfd->panel_info.type == MDDI_PANEL) ||
102 (mfd->panel_info.type == EXT_MDDI_PANEL)) {
103 dma2_cfg_reg |= DMA_OUT_SEL_MDDI;
104 mddi_dest = TRUE;
105
106 if (mfd->panel_info.type == MDDI_PANEL) {
107 mdp_total_vdopkts++;
108 if (mfd->panel_info.pdest == DISPLAY_1) {
109 dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
110 mddi_ld_param = 0;
111#ifdef MDDI_HOST_WINDOW_WORKAROUND
112 mddi_window_adjust(mfd, iBuf->dma_x,
113 iBuf->dma_w - 1, iBuf->dma_y,
114 iBuf->dma_h - 1);
115#endif
116 } else {
117 dma2_cfg_reg |=
118 DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY;
119 mddi_ld_param = 1;
120#ifdef MDDI_HOST_WINDOW_WORKAROUND
121 mddi_window_adjust(mfd, iBuf->dma_x,
122 iBuf->dma_w - 1, iBuf->dma_y,
123 iBuf->dma_h - 1);
124#endif
125 }
126 } else {
127 dma2_cfg_reg |= DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL;
128 mddi_ld_param = 2;
129 }
130 } else {
131 if (mfd->panel_info.pdest == DISPLAY_1) {
132 dma2_cfg_reg |= DMA_AHBM_LCD_SEL_PRIMARY;
133 outp32(MDP_EBI2_LCD0, mfd->data_port_phys);
134 } else {
135 dma2_cfg_reg |= DMA_AHBM_LCD_SEL_SECONDARY;
136 outp32(MDP_EBI2_LCD1, mfd->data_port_phys);
137 }
138 }
139
140 dma2_cfg_reg |= DMA_DITHER_EN;
141
142 src = (uint8 *) iBuf->buf;
143
144 src += iBuf->dma_x * outBpp + iBuf->dma_y * ystride;
145
146 mdp_curr_dma2_update_width = iBuf->dma_w;
147 mdp_curr_dma2_update_height = iBuf->dma_h;
148
149
150 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
151
152#ifdef CONFIG_FB_MSM_MDP22
153 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0184,
154 (iBuf->dma_h << 16 | iBuf->dma_w));
155 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0188, src);
156 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x018C, ystride);
157#else
158 MDP_OUTP(MDP_BASE + 0x90004, (iBuf->dma_h << 16 | iBuf->dma_w));
159 MDP_OUTP(MDP_BASE + 0x90008, src);
160 MDP_OUTP(MDP_BASE + 0x9000c, ystride);
161#endif
162
163 if (mfd->panel_info.bpp == 18) {
164 dma2_cfg_reg |= DMA_DSTC0G_6BITS |
165 DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
166 } else {
167 dma2_cfg_reg |= DMA_DSTC0G_6BITS |
168 DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
169 }
170
171 if (mddi_dest) {
172#ifdef CONFIG_FB_MSM_MDP22
173 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0194,
174 (iBuf->dma_y << 16) | iBuf->dma_x);
175 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0, mddi_ld_param);
176 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4,
177 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
178#else
179 MDP_OUTP(MDP_BASE + 0x90010, (iBuf->dma_y << 16) | iBuf->dma_x);
180 MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
181 MDP_OUTP(MDP_BASE + 0x00094,
182 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
183#endif
184 } else {
185
186 pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w,
187 iBuf->dma_h);
188 }
189
190
191#ifdef MDP_HW_VSYNC
192 MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
193
194 if ((mfd->use_mdp_vsync) &&
195 (mfd->ibuf.vsync_enable) && (mfd->panel_info.lcd.vsync_enable)) {
196 uint32 start_y;
197
198 if (vsync_start_y_adjust <= iBuf->dma_y)
199 start_y = iBuf->dma_y - vsync_start_y_adjust;
200 else
201 start_y =
202 (mfd->total_lcd_lines - 1) - (vsync_start_y_adjust -
203 iBuf->dma_y);
204
205
206
207
208
209 MDP_OUTP(MDP_BASE + 0x210, start_y);
210 MDP_OUTP(MDP_BASE + 0x20c, 1);
211 } else {
212 MDP_OUTP(MDP_BASE + 0x20c, 0);
213 }
214#else
215#ifdef CONFIG_FB_MSM_MDP22
216 MDP_OUTP(MDP_CMD_DEBUG_ACCESS_BASE + 0x0180, dma2_cfg_reg);
217#else
218 MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
219#endif
220#endif
221
222
223 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
224}
225
226static ktime_t vt = { 0 };
227int mdp_usec_diff_threshold = 100;
228int mdp_expected_usec_wait;
229
230enum hrtimer_restart mdp_dma2_vsync_hrtimer_handler(struct hrtimer *ht)
231{
232 struct msm_fb_data_type *mfd = NULL;
233
234 mfd = container_of(ht, struct msm_fb_data_type, dma_hrtimer);
235
236 mdp_pipe_kickoff(MDP_DMA2_TERM, mfd);
237
238 if (msm_fb_debug_enabled) {
239 ktime_t t;
240 int usec_diff;
241 int actual_wait;
242
243 t = ktime_get_real();
244
245 actual_wait =
246 (t.tv.sec - vt.tv.sec) * 1000000 + (t.tv.nsec -
247 vt.tv.nsec) / 1000;
248 usec_diff = actual_wait - mdp_expected_usec_wait;
249
250 if ((mdp_usec_diff_threshold < usec_diff) || (usec_diff < 0))
251 MSM_FB_DEBUG
252 ("HRT Diff = %d usec Exp=%d usec Act=%d usec\n",
253 usec_diff, mdp_expected_usec_wait, actual_wait);
254 }
255
256 return HRTIMER_NORESTART;
257}
258
259static void mdp_dma_schedule(struct msm_fb_data_type *mfd, uint32 term)
260{
261
262
263
264
265 int32 mdp_lcd_rd_cnt;
266 uint32 usec_wait_time;
267 uint32 start_y;
268
269
270
271
272
273
274
275
276 if (term == MDP_DMA2_TERM)
277 mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
278
279 mdp_dma2_update_time_in_usec =
280 MDP_KTIME2USEC(mdp_dma2_last_update_time);
281
282 if ((!mfd->ibuf.vsync_enable) || (!mfd->panel_info.lcd.vsync_enable)
283 || (mfd->use_mdp_vsync)) {
284 mdp_pipe_kickoff(term, mfd);
285 return;
286 }
287
288
289
290 mdp_lcd_rd_cnt = mdp_get_lcd_line_counter(mfd);
291 if (mdp_dma2_update_time_in_usec != 0) {
292 uint32 num, den;
293
294
295
296
297
298
299
300 num = mdp_last_dma2_update_width * mdp_last_dma2_update_height;
301 den =
302 (((mfd->panel_info.lcd.refx100 * mfd->total_lcd_lines) /
303 1000) * (mdp_dma2_update_time_in_usec / 100)) / 1000;
304
305 if (den == 0)
306 mfd->vsync_width_boundary[mdp_last_dma2_update_width] =
307 mfd->panel_info.xres + 1;
308 else
309 mfd->vsync_width_boundary[mdp_last_dma2_update_width] =
310 (int)(num / den);
311 }
312
313 if (mfd->vsync_width_boundary[mdp_last_dma2_update_width] >
314 mdp_curr_dma2_update_width) {
315
316 mdp_lcd_rd_cnt += mdp_lcd_rd_cnt_offset_fast;
317 } else {
318
319 mdp_lcd_rd_cnt -= mdp_lcd_rd_cnt_offset_slow;
320 }
321
322 if (mdp_lcd_rd_cnt < 0)
323 mdp_lcd_rd_cnt = mfd->total_lcd_lines + mdp_lcd_rd_cnt;
324 else if (mdp_lcd_rd_cnt > mfd->total_lcd_lines)
325 mdp_lcd_rd_cnt = mdp_lcd_rd_cnt - mfd->total_lcd_lines - 1;
326
327
328 start_y = mfd->ibuf.dma_y;
329
330
331 if (start_y > mdp_lcd_rd_cnt) {
332
333
334
335
336
337 if ((start_y - mdp_lcd_rd_cnt) <=
338 mdp_vsync_usec_wait_line_too_short)
339 usec_wait_time = 0;
340 else
341 usec_wait_time =
342 ((start_y -
343 mdp_lcd_rd_cnt) * 1000000) /
344 ((mfd->total_lcd_lines *
345 mfd->panel_info.lcd.refx100) / 100);
346 } else {
347 if ((start_y + (mfd->total_lcd_lines - mdp_lcd_rd_cnt)) <=
348 mdp_vsync_usec_wait_line_too_short)
349 usec_wait_time = 0;
350 else
351 usec_wait_time =
352 ((start_y +
353 (mfd->total_lcd_lines -
354 mdp_lcd_rd_cnt)) * 1000000) /
355 ((mfd->total_lcd_lines *
356 mfd->panel_info.lcd.refx100) / 100);
357 }
358
359 mdp_last_dma2_update_width = mdp_curr_dma2_update_width;
360 mdp_last_dma2_update_height = mdp_curr_dma2_update_height;
361
362 if (usec_wait_time == 0) {
363 mdp_pipe_kickoff(term, mfd);
364 } else {
365 ktime_t wait_time;
366
367 wait_time.tv.sec = 0;
368 wait_time.tv.nsec = usec_wait_time * 1000;
369
370 if (msm_fb_debug_enabled) {
371 vt = ktime_get_real();
372 mdp_expected_usec_wait = usec_wait_time;
373 }
374 hrtimer_start(&mfd->dma_hrtimer, wait_time, HRTIMER_MODE_REL);
375 }
376}
377
378#ifdef MDDI_HOST_WINDOW_WORKAROUND
379void mdp_dma2_update(struct msm_fb_data_type *mfd)
380{
381 MDPIBUF *iBuf;
382 uint32 upper_height;
383
384 if (mfd->panel.type == EXT_MDDI_PANEL) {
385 mdp_dma2_update_sub(mfd);
386 return;
387 }
388
389 iBuf = &mfd->ibuf;
390
391 upper_height =
392 (uint32) mddi_assign_pkt_height((uint16) iBuf->dma_w,
393 (uint16) iBuf->dma_h, 18);
394
395 if (upper_height >= iBuf->dma_h) {
396 mdp_dma2_update_sub(mfd);
397 } else {
398 MDPIBUF lower_height;
399
400
401 lower_height = iBuf->dma_h - upper_height;
402 iBuf->dma_h = upper_height;
403 mdp_dma2_update_sub(mfd);
404
405
406 iBuf->dma_h = lower_height;
407 iBuf->dma_y += lower_height;
408 iBuf->vsync_enable = FALSE;
409 mdp_dma2_update_sub(mfd);
410 }
411}
412
413void mdp_dma2_update_sub(struct msm_fb_data_type *mfd)
414#else
415void mdp_dma2_update(struct msm_fb_data_type *mfd)
416#endif
417{
418 down(&mfd->dma->mutex);
419 if ((mfd) && (!mfd->dma->busy) && (mfd->panel_power_on)) {
420 down(&mfd->sem);
421 mfd->ibuf_flushed = TRUE;
422 mdp_dma2_update_lcd(mfd);
423
424 mdp_enable_irq(MDP_DMA2_TERM);
425 mfd->dma->busy = TRUE;
426 INIT_COMPLETION(mfd->dma->comp);
427
428
429 mdp_dma_schedule(mfd, MDP_DMA2_TERM);
430 up(&mfd->sem);
431
432
433 wait_for_completion_killable(&mfd->dma->comp);
434 mdp_disable_irq(MDP_DMA2_TERM);
435
436
437 if (mfd->pan_waiting) {
438 mfd->pan_waiting = FALSE;
439 complete(&mfd->pan_comp);
440 }
441 }
442 up(&mfd->dma->mutex);
443}
444
445void mdp_lcd_update_workqueue_handler(struct work_struct *work)
446{
447 struct msm_fb_data_type *mfd = NULL;
448
449 mfd = container_of(work, struct msm_fb_data_type, dma_update_worker);
450 if (mfd)
451 mfd->dma_fnc(mfd);
452}
453
454void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty,
455 boolean sync)
456{
457 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
458 MDPIBUF *iBuf;
459 int bpp = info->var.bits_per_pixel / 8;
460
461 down(&mfd->sem);
462 iBuf = &mfd->ibuf;
463 iBuf->buf = (uint8 *) info->fix.smem_start;
464 iBuf->buf += info->var.xoffset * bpp +
465 info->var.yoffset * info->fix.line_length;
466
467 iBuf->ibuf_width = info->var.xres_virtual;
468 iBuf->bpp = bpp;
469
470 iBuf->vsync_enable = sync;
471
472 if (dirty) {
473
474
475
476
477 iBuf->dma_x = dirty->xoffset % info->var.xres;
478 iBuf->dma_y = dirty->yoffset % info->var.yres;
479 iBuf->dma_w = dirty->width;
480 iBuf->dma_h = dirty->height;
481 } else {
482 iBuf->dma_x = 0;
483 iBuf->dma_y = 0;
484 iBuf->dma_w = info->var.xres;
485 iBuf->dma_h = info->var.yres;
486 }
487 mfd->ibuf_flushed = FALSE;
488 up(&mfd->sem);
489}
490
491void mdp_set_offset_info(struct fb_info *info, uint32 addr, uint32 sync)
492{
493 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
494 MDPIBUF *iBuf;
495
496 int bpp = info->var.bits_per_pixel / 8;
497
498 down(&mfd->sem);
499 iBuf = &mfd->ibuf;
500 iBuf->ibuf_width = info->var.xres_virtual;
501 iBuf->bpp = bpp;
502 iBuf->vsync_enable = sync;
503 iBuf->dma_x = 0;
504 iBuf->dma_y = 0;
505 iBuf->dma_w = info->var.xres;
506 iBuf->dma_h = info->var.yres;
507 iBuf->buf = (uint8 *) addr;
508
509 mfd->ibuf_flushed = FALSE;
510 up(&mfd->sem);
511}
512
513void mdp_dma_pan_update(struct fb_info *info)
514{
515 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
516 MDPIBUF *iBuf;
517
518 iBuf = &mfd->ibuf;
519
520 if (mfd->sw_currently_refreshing) {
521
522 mfd->pan_waiting = TRUE;
523 if (!mfd->ibuf_flushed) {
524 wait_for_completion_killable(&mfd->pan_comp);
525 }
526
527 mfd->pan_waiting = TRUE;
528 wait_for_completion_killable(&mfd->pan_comp);
529 } else
530 mfd->dma_fnc(mfd);
531}
532
533void mdp_refresh_screen(unsigned long data)
534{
535 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
536
537 if ((mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
538 init_timer(&mfd->refresh_timer);
539 mfd->refresh_timer.function = mdp_refresh_screen;
540 mfd->refresh_timer.data = data;
541
542 if (mfd->dma->busy)
543
544 mfd->refresh_timer.expires = jiffies + (HZ / 1000);
545 else
546 mfd->refresh_timer.expires =
547 jiffies + mfd->refresh_timer_duration;
548
549 add_timer(&mfd->refresh_timer);
550
551 if (!mfd->dma->busy) {
552 if (!queue_work(mdp_dma_wq, &mfd->dma_update_worker)) {
553 MSM_FB_DEBUG("mdp_dma: can't queue_work! -> \
554 MDP/MDDI/LCD clock speed needs to be increased\n");
555 }
556 }
557 } else {
558 if (!mfd->hw_refresh)
559 complete(&mfd->refresher_comp);
560 }
561}
562