1 /* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
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.
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.
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
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/spinlock.h>
25 #include <linux/hrtimer.h>
26 #include <linux/vmalloc.h>
27 #include <linux/clk.h>
29 #include <mach/hardware.h>
32 #include <asm/system.h>
33 #include <asm/mach-types.h>
34 #include <linux/semaphore.h>
35 #include <linux/uaccess.h>
36 #include <mach/gpio.h>
42 #ifdef CONFIG_FB_MSM_MDP40
43 #define MDP_SYNC_CFG_0 0x100
44 #define MDP_SYNC_STATUS_0 0x10c
45 #define MDP_PRIM_VSYNC_OUT_CTRL 0x118
46 #define MDP_PRIM_VSYNC_INIT_VAL 0x128
48 #define MDP_SYNC_CFG_0 0x300
49 #define MDP_SYNC_STATUS_0 0x30c
50 #define MDP_PRIM_VSYNC_OUT_CTRL 0x318
51 #define MDP_PRIM_VSYNC_INIT_VAL 0x328
54 extern mddi_lcd_type mddi_lcd_idx
;
55 extern spinlock_t mdp_spin_lock
;
56 extern struct workqueue_struct
*mdp_vsync_wq
;
58 extern int vsync_mode
;
61 int vsync_above_th
= 4;
62 int vsync_start_th
= 1;
65 struct clk
*mdp_vsync_clk
;
67 void mdp_hw_vsync_clk_enable(struct msm_fb_data_type
*mfd
)
69 if (mfd
->use_mdp_vsync
)
70 clk_enable(mdp_vsync_clk
);
73 void mdp_hw_vsync_clk_disable(struct msm_fb_data_type
*mfd
)
75 if (mfd
->use_mdp_vsync
)
76 clk_disable(mdp_vsync_clk
);
80 static void mdp_set_vsync(unsigned long data
)
82 struct msm_fb_data_type
*mfd
= (struct msm_fb_data_type
*)data
;
83 struct msm_fb_panel_data
*pdata
= NULL
;
85 pdata
= (struct msm_fb_panel_data
*)mfd
->pdev
->dev
.platform_data
;
87 if ((pdata
) && (pdata
->set_vsync_notifier
== NULL
))
90 init_timer(&mfd
->vsync_resync_timer
);
91 mfd
->vsync_resync_timer
.function
= mdp_set_vsync
;
92 mfd
->vsync_resync_timer
.data
= data
;
93 mfd
->vsync_resync_timer
.expires
=
94 jiffies
+ mfd
->panel_info
.lcd
.vsync_notifier_period
;
95 add_timer(&mfd
->vsync_resync_timer
);
97 if ((mfd
->panel_info
.lcd
.vsync_enable
) && (mfd
->panel_power_on
)
98 && (!mfd
->vsync_handler_pending
)) {
99 mfd
->vsync_handler_pending
= TRUE
;
100 if (!queue_work(mdp_vsync_wq
, &mfd
->vsync_resync_worker
)) {
102 ("mdp_set_vsync: can't queue_work! -> needs to increase vsync_resync_timer_duration\n");
106 ("mdp_set_vsync failed! EN:%d PWR:%d PENDING:%d\n",
107 mfd
->panel_info
.lcd
.vsync_enable
, mfd
->panel_power_on
,
108 mfd
->vsync_handler_pending
);
112 static void mdp_vsync_handler(void *data
)
114 struct msm_fb_data_type
*mfd
= (struct msm_fb_data_type
*)data
;
116 if (mfd
->use_mdp_vsync
) {
118 if (mfd
->panel_power_on
)
119 MDP_OUTP(MDP_BASE
+ MDP_SYNC_STATUS_0
, vsync_load_cnt
);
121 mdp_pipe_ctrl(MDP_CMD_BLOCK
, MDP_BLOCK_POWER_OFF
, TRUE
);
124 mfd
->last_vsync_timetick
= ktime_get_real();
127 mfd
->vsync_handler_pending
= FALSE
;
130 irqreturn_t
mdp_hw_vsync_handler_proxy(int irq
, void *data
)
133 * ToDo: tried enabling/disabling GPIO MDP HW VSYNC interrupt
134 * but getting inaccurate timing in mdp_vsync_handler()
135 * disable_irq(MDP_HW_VSYNC_IRQ);
137 mdp_vsync_handler(data
);
143 static void mdp_set_sync_cfg_0(struct msm_fb_data_type
*mfd
, int vsync_cnt
)
147 cfg
= mfd
->total_lcd_lines
- 1;
148 cfg
<<= MDP_SYNCFG_HGT_LOC
;
149 if (mfd
->panel_info
.lcd
.hw_vsync_mode
)
150 cfg
|= MDP_SYNCFG_VSYNC_EXT_EN
;
151 cfg
|= (MDP_SYNCFG_VSYNC_INT_EN
| vsync_cnt
);
153 MDP_OUTP(MDP_BASE
+ MDP_SYNC_CFG_0
, cfg
);
157 void mdp_config_vsync(struct msm_fb_data_type
*mfd
)
160 /* vsync on primary lcd only for now */
161 if ((mfd
->dest
!= DISPLAY_LCD
) || (mfd
->panel_info
.pdest
!= DISPLAY_1
)
166 if (mfd
->panel_info
.lcd
.vsync_enable
) {
167 mfd
->total_porch_lines
= mfd
->panel_info
.lcd
.v_back_porch
+
168 mfd
->panel_info
.lcd
.v_front_porch
+
169 mfd
->panel_info
.lcd
.v_pulse_width
;
170 mfd
->total_lcd_lines
=
171 mfd
->panel_info
.yres
+ mfd
->total_porch_lines
;
172 mfd
->lcd_ref_usec_time
=
173 100000000 / mfd
->panel_info
.lcd
.refx100
;
174 mfd
->vsync_handler_pending
= FALSE
;
175 mfd
->last_vsync_timetick
.tv
.sec
= 0;
176 mfd
->last_vsync_timetick
.tv
.nsec
= 0;
179 if (mdp_vsync_clk
== NULL
)
180 mdp_vsync_clk
= clk_get(NULL
, "mdp_vsync_clk");
182 if (IS_ERR(mdp_vsync_clk
)) {
183 printk(KERN_ERR
"error: can't get mdp_vsync_clk!\n");
184 mfd
->use_mdp_vsync
= 0;
186 mfd
->use_mdp_vsync
= 1;
188 if (mfd
->use_mdp_vsync
) {
189 uint32 vsync_cnt_cfg
, vsync_cnt_cfg_dem
;
190 uint32 mdp_vsync_clk_speed_hz
;
192 mdp_vsync_clk_speed_hz
= clk_get_rate(mdp_vsync_clk
);
194 if (mdp_vsync_clk_speed_hz
== 0) {
195 mfd
->use_mdp_vsync
= 0;
198 * Do this calculation in 2 steps for
199 * rounding uint32 properly.
202 (mfd
->panel_info
.lcd
.refx100
*
203 mfd
->total_lcd_lines
) / 100;
205 (mdp_vsync_clk_speed_hz
) /
208 /* MDP cmd block enable */
209 mdp_pipe_ctrl(MDP_CMD_BLOCK
, MDP_BLOCK_POWER_ON
,
211 mdp_hw_vsync_clk_enable(mfd
);
213 mdp_set_sync_cfg_0(mfd
, vsync_cnt_cfg
);
216 * load the last line + 1 to be in the
219 vsync_load_cnt
= mfd
->panel_info
.yres
;
221 /* line counter init value at the next pulse */
222 MDP_OUTP(MDP_BASE
+ MDP_PRIM_VSYNC_INIT_VAL
,
226 * external vsync source pulse width and
229 MDP_OUTP(MDP_BASE
+ MDP_PRIM_VSYNC_OUT_CTRL
,
234 MDP_OUTP(MDP_BASE
+ 0x200,
235 (vsync_above_th
<< 16) |
238 mdp_hw_vsync_clk_disable(mfd
);
239 /* MDP cmd block disable */
240 mdp_pipe_ctrl(MDP_CMD_BLOCK
,
241 MDP_BLOCK_POWER_OFF
, FALSE
);
245 mfd
->use_mdp_vsync
= 0;
246 hrtimer_init(&mfd
->dma_hrtimer
, CLOCK_MONOTONIC
,
248 mfd
->dma_hrtimer
.function
= mdp_dma2_vsync_hrtimer_handler
;
249 mfd
->vsync_width_boundary
= vmalloc(mfd
->panel_info
.xres
* 4);
252 mfd
->channel_irq
= 0;
253 if (mfd
->panel_info
.lcd
.hw_vsync_mode
) {
254 u32 vsync_gpio
= mfd
->vsync_gpio
;
257 if (vsync_gpio
== -1) {
258 MSM_FB_INFO("vsync_gpio not defined!\n");
262 ret
= gpio_tlmm_config(GPIO_CFG
264 (mfd
->use_mdp_vsync
) ? 1 : 0,
272 if (!mfd
->use_mdp_vsync
) {
273 mfd
->channel_irq
= MSM_GPIO_TO_INT(vsync_gpio
);
276 &mdp_hw_vsync_handler_proxy
,
277 IRQF_TRIGGER_FALLING
, "VSYNC_GPIO",
280 ("irq=%d failed! vsync_gpio=%d\n",
288 mdp_set_vsync((unsigned long)mfd
);
294 if (mfd
->vsync_width_boundary
)
295 vfree(mfd
->vsync_width_boundary
);
296 mfd
->panel_info
.lcd
.vsync_enable
= FALSE
;
297 printk(KERN_ERR
"%s: failed!\n", __func__
);
300 void mdp_vsync_resync_workqueue_handler(struct work_struct
*work
)
302 struct msm_fb_data_type
*mfd
= NULL
;
303 int vsync_fnc_enabled
= FALSE
;
304 struct msm_fb_panel_data
*pdata
= NULL
;
306 mfd
= container_of(work
, struct msm_fb_data_type
, vsync_resync_worker
);
309 if (mfd
->panel_power_on
) {
311 (struct msm_fb_panel_data
*)mfd
->pdev
->dev
.
315 * we need to turn on MDP power if it uses MDP vsync
316 * HW block in SW mode
318 if ((!mfd
->panel_info
.lcd
.hw_vsync_mode
) &&
319 (mfd
->use_mdp_vsync
) &&
320 (pdata
) && (pdata
->set_vsync_notifier
!= NULL
)) {
322 * enable pwr here since we can't enable it in
323 * vsync callback in isr mode
325 mdp_pipe_ctrl(MDP_CMD_BLOCK
, MDP_BLOCK_POWER_ON
,
329 if (pdata
->set_vsync_notifier
!= NULL
) {
330 vsync_fnc_enabled
= TRUE
;
331 pdata
->set_vsync_notifier(mdp_vsync_handler
,
337 if ((mfd
) && (!vsync_fnc_enabled
))
338 mfd
->vsync_handler_pending
= FALSE
;
341 boolean
mdp_hw_vsync_set_handler(msm_fb_vsync_handler_type handler
, void *data
)
344 * ToDo: tried enabling/disabling GPIO MDP HW VSYNC interrupt
345 * but getting inaccurate timing in mdp_vsync_handler()
346 * enable_irq(MDP_HW_VSYNC_IRQ);
352 uint32
mdp_get_lcd_line_counter(struct msm_fb_data_type
*mfd
)
354 uint32 elapsed_usec_time
;
356 ktime_t last_vsync_timetick_local
;
360 if ((!mfd
->panel_info
.lcd
.vsync_enable
) || (!vsync_mode
))
363 spin_lock_irqsave(&mdp_spin_lock
, flag
);
364 last_vsync_timetick_local
= mfd
->last_vsync_timetick
;
365 spin_unlock_irqrestore(&mdp_spin_lock
, flag
);
367 curr_time
= ktime_get_real();
369 ((curr_time
.tv
.sec
- last_vsync_timetick_local
.tv
.sec
) * 1000000) +
370 ((curr_time
.tv
.nsec
- last_vsync_timetick_local
.tv
.nsec
) / 1000);
372 elapsed_usec_time
= elapsed_usec_time
% mfd
->lcd_ref_usec_time
;
374 /* lcd line calculation referencing to line counter = 0 */
376 (elapsed_usec_time
* mfd
->total_lcd_lines
) / mfd
->lcd_ref_usec_time
;
378 /* lcd line adjusment referencing to the actual line counter at vsync */
380 (mfd
->total_lcd_lines
- mfd
->panel_info
.lcd
.v_back_porch
+
381 lcd_line
) % (mfd
->total_lcd_lines
+ 1);
383 if (lcd_line
> mfd
->total_lcd_lines
) {
385 ("mdp_get_lcd_line_counter: mdp_lcd_rd_cnt >= mfd->total_lcd_lines error!\n");