]>
Commit | Line | Data |
---|---|---|
9d200153 SM |
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 | #include <linux/delay.h> | |
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 "mdp4.h" | |
39 | ||
40 | #ifdef CONFIG_FB_MSM_MDP40 | |
41 | #define LCDC_BASE 0xC0000 | |
42 | #define DTV_BASE 0xD0000 | |
43 | #define DMA_E_BASE 0xB0000 | |
44 | #else | |
45 | #define LCDC_BASE 0xE0000 | |
46 | #endif | |
47 | ||
48 | #define DMA_P_BASE 0x90000 | |
49 | ||
50 | extern spinlock_t mdp_spin_lock; | |
51 | #ifndef CONFIG_FB_MSM_MDP40 | |
52 | extern uint32 mdp_intr_mask; | |
53 | #endif | |
54 | ||
55 | int first_pixel_start_x; | |
56 | int first_pixel_start_y; | |
57 | ||
58 | int mdp_lcdc_on(struct platform_device *pdev) | |
59 | { | |
60 | int lcdc_width; | |
61 | int lcdc_height; | |
62 | int lcdc_bpp; | |
63 | int lcdc_border_clr; | |
64 | int lcdc_underflow_clr; | |
65 | int lcdc_hsync_skew; | |
66 | ||
67 | int hsync_period; | |
68 | int hsync_ctrl; | |
69 | int vsync_period; | |
70 | int display_hctl; | |
71 | int display_v_start; | |
72 | int display_v_end; | |
73 | int active_hctl; | |
74 | int active_h_start; | |
75 | int active_h_end; | |
76 | int active_v_start; | |
77 | int active_v_end; | |
78 | int ctrl_polarity; | |
79 | int h_back_porch; | |
80 | int h_front_porch; | |
81 | int v_back_porch; | |
82 | int v_front_porch; | |
83 | int hsync_pulse_width; | |
84 | int vsync_pulse_width; | |
85 | int hsync_polarity; | |
86 | int vsync_polarity; | |
87 | int data_en_polarity; | |
88 | int hsync_start_x; | |
89 | int hsync_end_x; | |
90 | uint8 *buf; | |
91 | int bpp; | |
92 | uint32 dma2_cfg_reg; | |
93 | struct fb_info *fbi; | |
94 | struct fb_var_screeninfo *var; | |
95 | struct msm_fb_data_type *mfd; | |
96 | uint32 dma_base; | |
97 | uint32 timer_base = LCDC_BASE; | |
98 | uint32 block = MDP_DMA2_BLOCK; | |
99 | int ret; | |
100 | ||
101 | mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); | |
102 | ||
103 | if (!mfd) | |
104 | return -ENODEV; | |
105 | ||
106 | if (mfd->key != MFD_KEY) | |
107 | return -EINVAL; | |
108 | ||
109 | fbi = mfd->fbi; | |
110 | var = &fbi->var; | |
111 | ||
112 | /* MDP cmd block enable */ | |
113 | mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); | |
114 | ||
115 | bpp = fbi->var.bits_per_pixel / 8; | |
116 | buf = (uint8 *) fbi->fix.smem_start; | |
117 | buf += fbi->var.xoffset * bpp + fbi->var.yoffset * fbi->fix.line_length; | |
118 | ||
119 | dma2_cfg_reg = DMA_PACK_ALIGN_LSB | DMA_DITHER_EN | DMA_OUT_SEL_LCDC; | |
120 | ||
121 | if (mfd->fb_imgType == MDP_BGR_565) | |
122 | dma2_cfg_reg |= DMA_PACK_PATTERN_BGR; | |
123 | else | |
124 | dma2_cfg_reg |= DMA_PACK_PATTERN_RGB; | |
125 | ||
126 | if (bpp == 2) | |
127 | dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB565; | |
128 | else if (bpp == 3) | |
129 | dma2_cfg_reg |= DMA_IBUF_FORMAT_RGB888; | |
130 | else | |
131 | dma2_cfg_reg |= DMA_IBUF_FORMAT_xRGB8888_OR_ARGB8888; | |
132 | ||
133 | switch (mfd->panel_info.bpp) { | |
134 | case 24: | |
135 | dma2_cfg_reg |= DMA_DSTC0G_8BITS | | |
136 | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; | |
137 | break; | |
138 | ||
139 | case 18: | |
140 | dma2_cfg_reg |= DMA_DSTC0G_6BITS | | |
141 | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; | |
142 | break; | |
143 | ||
144 | case 16: | |
145 | dma2_cfg_reg |= DMA_DSTC0G_6BITS | | |
146 | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; | |
147 | break; | |
148 | ||
149 | default: | |
150 | printk(KERN_ERR "mdp lcdc can't support format %d bpp!\n", | |
151 | mfd->panel_info.bpp); | |
152 | return -ENODEV; | |
153 | } | |
154 | ||
155 | /* DMA register config */ | |
156 | ||
157 | dma_base = DMA_P_BASE; | |
158 | ||
159 | #ifdef CONFIG_FB_MSM_MDP40 | |
160 | if (mfd->panel.type == HDMI_PANEL) | |
161 | dma_base = DMA_E_BASE; | |
162 | #endif | |
163 | ||
164 | /* starting address */ | |
165 | MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf); | |
166 | /* active window width and height */ | |
167 | MDP_OUTP(MDP_BASE + dma_base + 0x4, ((fbi->var.yres) << 16) | | |
168 | (fbi->var.xres)); | |
169 | /* buffer ystride */ | |
170 | MDP_OUTP(MDP_BASE + dma_base + 0xc, fbi->fix.line_length); | |
171 | /* x/y coordinate = always 0 for lcdc */ | |
172 | MDP_OUTP(MDP_BASE + dma_base + 0x10, 0); | |
173 | /* dma config */ | |
174 | MDP_OUTP(MDP_BASE + dma_base, dma2_cfg_reg); | |
175 | ||
176 | /* | |
177 | * LCDC timing setting | |
178 | */ | |
179 | h_back_porch = var->left_margin; | |
180 | h_front_porch = var->right_margin; | |
181 | v_back_porch = var->upper_margin; | |
182 | v_front_porch = var->lower_margin; | |
183 | hsync_pulse_width = var->hsync_len; | |
184 | vsync_pulse_width = var->vsync_len; | |
185 | lcdc_border_clr = mfd->panel_info.lcdc.border_clr; | |
186 | lcdc_underflow_clr = mfd->panel_info.lcdc.underflow_clr; | |
187 | lcdc_hsync_skew = mfd->panel_info.lcdc.hsync_skew; | |
188 | ||
189 | lcdc_width = mfd->panel_info.xres; | |
190 | lcdc_height = mfd->panel_info.yres; | |
191 | lcdc_bpp = mfd->panel_info.bpp; | |
192 | ||
193 | hsync_period = | |
194 | hsync_pulse_width + h_back_porch + lcdc_width + h_front_porch; | |
195 | hsync_ctrl = (hsync_period << 16) | hsync_pulse_width; | |
196 | hsync_start_x = hsync_pulse_width + h_back_porch; | |
197 | hsync_end_x = hsync_period - h_front_porch - 1; | |
198 | display_hctl = (hsync_end_x << 16) | hsync_start_x; | |
199 | ||
200 | vsync_period = | |
201 | (vsync_pulse_width + v_back_porch + lcdc_height + | |
202 | v_front_porch) * hsync_period; | |
203 | display_v_start = | |
204 | (vsync_pulse_width + v_back_porch) * hsync_period + lcdc_hsync_skew; | |
205 | display_v_end = | |
206 | vsync_period - (v_front_porch * hsync_period) + lcdc_hsync_skew - 1; | |
207 | ||
208 | if (lcdc_width != var->xres) { | |
209 | active_h_start = hsync_start_x + first_pixel_start_x; | |
210 | active_h_end = active_h_start + var->xres - 1; | |
211 | active_hctl = | |
212 | ACTIVE_START_X_EN | (active_h_end << 16) | active_h_start; | |
213 | } else { | |
214 | active_hctl = 0; | |
215 | } | |
216 | ||
217 | if (lcdc_height != var->yres) { | |
218 | active_v_start = | |
219 | display_v_start + first_pixel_start_y * hsync_period; | |
220 | active_v_end = active_v_start + (var->yres) * hsync_period - 1; | |
221 | active_v_start |= ACTIVE_START_Y_EN; | |
222 | } else { | |
223 | active_v_start = 0; | |
224 | active_v_end = 0; | |
225 | } | |
226 | ||
227 | ||
228 | #ifdef CONFIG_FB_MSM_MDP40 | |
229 | if (mfd->panel.type == HDMI_PANEL) { | |
230 | block = MDP_DMA_E_BLOCK; | |
231 | timer_base = DTV_BASE; | |
232 | hsync_polarity = 0; | |
233 | vsync_polarity = 0; | |
234 | } else { | |
235 | hsync_polarity = 1; | |
236 | vsync_polarity = 1; | |
237 | } | |
238 | ||
239 | lcdc_underflow_clr |= 0x80000000; /* enable recovery */ | |
240 | #else | |
241 | hsync_polarity = 0; | |
242 | vsync_polarity = 0; | |
243 | #endif | |
244 | data_en_polarity = 0; | |
245 | ||
246 | ctrl_polarity = | |
247 | (data_en_polarity << 2) | (vsync_polarity << 1) | (hsync_polarity); | |
248 | ||
249 | MDP_OUTP(MDP_BASE + timer_base + 0x4, hsync_ctrl); | |
250 | MDP_OUTP(MDP_BASE + timer_base + 0x8, vsync_period); | |
251 | MDP_OUTP(MDP_BASE + timer_base + 0xc, vsync_pulse_width * hsync_period); | |
252 | if (timer_base == LCDC_BASE) { | |
253 | MDP_OUTP(MDP_BASE + timer_base + 0x10, display_hctl); | |
254 | MDP_OUTP(MDP_BASE + timer_base + 0x14, display_v_start); | |
255 | MDP_OUTP(MDP_BASE + timer_base + 0x18, display_v_end); | |
256 | MDP_OUTP(MDP_BASE + timer_base + 0x28, lcdc_border_clr); | |
257 | MDP_OUTP(MDP_BASE + timer_base + 0x2c, lcdc_underflow_clr); | |
258 | MDP_OUTP(MDP_BASE + timer_base + 0x30, lcdc_hsync_skew); | |
259 | MDP_OUTP(MDP_BASE + timer_base + 0x38, ctrl_polarity); | |
260 | MDP_OUTP(MDP_BASE + timer_base + 0x1c, active_hctl); | |
261 | MDP_OUTP(MDP_BASE + timer_base + 0x20, active_v_start); | |
262 | MDP_OUTP(MDP_BASE + timer_base + 0x24, active_v_end); | |
263 | } else { | |
264 | MDP_OUTP(MDP_BASE + timer_base + 0x18, display_hctl); | |
265 | MDP_OUTP(MDP_BASE + timer_base + 0x1c, display_v_start); | |
266 | MDP_OUTP(MDP_BASE + timer_base + 0x20, display_v_end); | |
267 | MDP_OUTP(MDP_BASE + timer_base + 0x40, lcdc_border_clr); | |
268 | MDP_OUTP(MDP_BASE + timer_base + 0x44, lcdc_underflow_clr); | |
269 | MDP_OUTP(MDP_BASE + timer_base + 0x48, lcdc_hsync_skew); | |
270 | MDP_OUTP(MDP_BASE + timer_base + 0x50, ctrl_polarity); | |
271 | MDP_OUTP(MDP_BASE + timer_base + 0x2c, active_hctl); | |
272 | MDP_OUTP(MDP_BASE + timer_base + 0x30, active_v_start); | |
273 | MDP_OUTP(MDP_BASE + timer_base + 0x38, active_v_end); | |
274 | } | |
275 | ||
276 | ret = panel_next_on(pdev); | |
277 | if (ret == 0) { | |
278 | /* enable LCDC block */ | |
279 | MDP_OUTP(MDP_BASE + timer_base, 1); | |
280 | mdp_pipe_ctrl(block, MDP_BLOCK_POWER_ON, FALSE); | |
281 | } | |
282 | /* MDP cmd block disable */ | |
283 | mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); | |
284 | ||
285 | return ret; | |
286 | } | |
287 | ||
288 | int mdp_lcdc_off(struct platform_device *pdev) | |
289 | { | |
290 | int ret = 0; | |
291 | struct msm_fb_data_type *mfd; | |
292 | uint32 timer_base = LCDC_BASE; | |
293 | uint32 block = MDP_DMA2_BLOCK; | |
294 | ||
295 | mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); | |
296 | ||
297 | #ifdef CONFIG_FB_MSM_MDP40 | |
298 | if (mfd->panel.type == HDMI_PANEL) { | |
299 | block = MDP_DMA_E_BLOCK; | |
300 | timer_base = DTV_BASE; | |
301 | } | |
302 | #endif | |
303 | ||
304 | /* MDP cmd block enable */ | |
305 | mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE); | |
306 | MDP_OUTP(MDP_BASE + timer_base, 0); | |
307 | /* MDP cmd block disable */ | |
308 | mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE); | |
309 | mdp_pipe_ctrl(block, MDP_BLOCK_POWER_OFF, FALSE); | |
310 | ||
311 | ret = panel_next_off(pdev); | |
312 | ||
313 | /* delay to make sure the last frame finishes */ | |
314 | mdelay(100); | |
315 | ||
316 | return ret; | |
317 | } | |
318 | ||
319 | void mdp_lcdc_update(struct msm_fb_data_type *mfd) | |
320 | { | |
321 | struct fb_info *fbi = mfd->fbi; | |
322 | uint8 *buf; | |
323 | int bpp; | |
324 | unsigned long flag; | |
325 | uint32 dma_base; | |
326 | int irq_block = MDP_DMA2_TERM; | |
327 | #ifdef CONFIG_FB_MSM_MDP40 | |
328 | int intr = INTR_DMA_P_DONE; | |
329 | #endif | |
330 | ||
331 | if (!mfd->panel_power_on) | |
332 | return; | |
333 | ||
334 | /* no need to power on cmd block since it's lcdc mode */ | |
335 | ||
336 | if (!mfd->ibuf.visible_swapped) { | |
337 | bpp = fbi->var.bits_per_pixel / 8; | |
338 | buf = (uint8 *) fbi->fix.smem_start; | |
339 | buf += fbi->var.xoffset * bpp + | |
340 | fbi->var.yoffset * fbi->fix.line_length; | |
341 | } else { | |
342 | /* we've done something to update the pointer. */ | |
343 | bpp = mfd->ibuf.bpp; | |
344 | buf = mfd->ibuf.buf; | |
345 | } | |
346 | ||
347 | dma_base = DMA_P_BASE; | |
348 | ||
349 | #ifdef CONFIG_FB_MSM_MDP40 | |
350 | if (mfd->panel.type == HDMI_PANEL) { | |
351 | intr = INTR_DMA_E_DONE; | |
352 | irq_block = MDP_DMA_E_TERM; | |
353 | dma_base = DMA_E_BASE; | |
354 | } | |
355 | #endif | |
356 | ||
357 | /* starting address */ | |
358 | MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf); | |
359 | ||
360 | /* enable LCDC irq */ | |
361 | spin_lock_irqsave(&mdp_spin_lock, flag); | |
362 | mdp_enable_irq(irq_block); | |
363 | INIT_COMPLETION(mfd->dma->comp); | |
364 | mfd->dma->waiting = TRUE; | |
365 | #ifdef CONFIG_FB_MSM_MDP40 | |
366 | outp32(MDP_INTR_CLEAR, intr); | |
367 | mdp_intr_mask |= intr; | |
368 | outp32(MDP_INTR_ENABLE, mdp_intr_mask); | |
369 | #else | |
370 | outp32(MDP_INTR_CLEAR, LCDC_FRAME_START); | |
371 | mdp_intr_mask |= LCDC_FRAME_START; | |
372 | outp32(MDP_INTR_ENABLE, mdp_intr_mask); | |
373 | #endif | |
374 | spin_unlock_irqrestore(&mdp_spin_lock, flag); | |
375 | ||
376 | if (mfd->ibuf.vsync_enable) | |
377 | wait_for_completion_killable(&mfd->dma->comp); | |
378 | mdp_disable_irq(irq_block); | |
379 | } |