• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/staging/msm/
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