diff options
Diffstat (limited to 'drivers/video/fbdev/msm/mdp_ppp.c')
-rw-r--r-- | drivers/video/fbdev/msm/mdp_ppp.c | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/drivers/video/fbdev/msm/mdp_ppp.c b/drivers/video/fbdev/msm/mdp_ppp.c new file mode 100644 index 000000000000..be6079cdfbb6 --- /dev/null +++ b/drivers/video/fbdev/msm/mdp_ppp.c | |||
@@ -0,0 +1,731 @@ | |||
1 | /* drivers/video/msm/mdp_ppp.c | ||
2 | * | ||
3 | * Copyright (C) 2007 QUALCOMM Incorporated | ||
4 | * Copyright (C) 2007 Google Incorporated | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | #include <linux/fb.h> | ||
16 | #include <linux/file.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/msm_mdp.h> | ||
19 | #include <linux/platform_data/video-msm_fb.h> | ||
20 | |||
21 | #include "mdp_hw.h" | ||
22 | #include "mdp_scale_tables.h" | ||
23 | |||
24 | #define DLOG(x...) do {} while (0) | ||
25 | |||
26 | #define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1) | ||
27 | static int downscale_y_table = MDP_DOWNSCALE_MAX; | ||
28 | static int downscale_x_table = MDP_DOWNSCALE_MAX; | ||
29 | |||
30 | struct mdp_regs { | ||
31 | uint32_t src0; | ||
32 | uint32_t src1; | ||
33 | uint32_t dst0; | ||
34 | uint32_t dst1; | ||
35 | uint32_t src_cfg; | ||
36 | uint32_t dst_cfg; | ||
37 | uint32_t src_pack; | ||
38 | uint32_t dst_pack; | ||
39 | uint32_t src_rect; | ||
40 | uint32_t dst_rect; | ||
41 | uint32_t src_ystride; | ||
42 | uint32_t dst_ystride; | ||
43 | uint32_t op; | ||
44 | uint32_t src_bpp; | ||
45 | uint32_t dst_bpp; | ||
46 | uint32_t edge; | ||
47 | uint32_t phasex_init; | ||
48 | uint32_t phasey_init; | ||
49 | uint32_t phasex_step; | ||
50 | uint32_t phasey_step; | ||
51 | }; | ||
52 | |||
53 | static uint32_t pack_pattern[] = { | ||
54 | PPP_ARRAY0(PACK_PATTERN) | ||
55 | }; | ||
56 | |||
57 | static uint32_t src_img_cfg[] = { | ||
58 | PPP_ARRAY1(CFG, SRC) | ||
59 | }; | ||
60 | |||
61 | static uint32_t dst_img_cfg[] = { | ||
62 | PPP_ARRAY1(CFG, DST) | ||
63 | }; | ||
64 | |||
65 | static uint32_t bytes_per_pixel[] = { | ||
66 | [MDP_RGB_565] = 2, | ||
67 | [MDP_RGB_888] = 3, | ||
68 | [MDP_XRGB_8888] = 4, | ||
69 | [MDP_ARGB_8888] = 4, | ||
70 | [MDP_RGBA_8888] = 4, | ||
71 | [MDP_BGRA_8888] = 4, | ||
72 | [MDP_RGBX_8888] = 4, | ||
73 | [MDP_Y_CBCR_H2V1] = 1, | ||
74 | [MDP_Y_CBCR_H2V2] = 1, | ||
75 | [MDP_Y_CRCB_H2V1] = 1, | ||
76 | [MDP_Y_CRCB_H2V2] = 1, | ||
77 | [MDP_YCRYCB_H2V1] = 2 | ||
78 | }; | ||
79 | |||
80 | static uint32_t dst_op_chroma[] = { | ||
81 | PPP_ARRAY1(CHROMA_SAMP, DST) | ||
82 | }; | ||
83 | |||
84 | static uint32_t src_op_chroma[] = { | ||
85 | PPP_ARRAY1(CHROMA_SAMP, SRC) | ||
86 | }; | ||
87 | |||
88 | static uint32_t bg_op_chroma[] = { | ||
89 | PPP_ARRAY1(CHROMA_SAMP, BG) | ||
90 | }; | ||
91 | |||
92 | static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs) | ||
93 | { | ||
94 | regs->dst0 += (req->dst_rect.w - | ||
95 | min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; | ||
96 | regs->dst1 += (req->dst_rect.w - | ||
97 | min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; | ||
98 | } | ||
99 | |||
100 | static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs) | ||
101 | { | ||
102 | regs->dst0 += (req->dst_rect.h - | ||
103 | min((uint32_t)16, req->dst_rect.h)) * | ||
104 | regs->dst_ystride; | ||
105 | regs->dst1 += (req->dst_rect.h - | ||
106 | min((uint32_t)16, req->dst_rect.h)) * | ||
107 | regs->dst_ystride; | ||
108 | } | ||
109 | |||
110 | static void blit_rotate(struct mdp_blit_req *req, | ||
111 | struct mdp_regs *regs) | ||
112 | { | ||
113 | if (req->flags == MDP_ROT_NOP) | ||
114 | return; | ||
115 | |||
116 | regs->op |= PPP_OP_ROT_ON; | ||
117 | if ((req->flags & MDP_ROT_90 || req->flags & MDP_FLIP_LR) && | ||
118 | !(req->flags & MDP_ROT_90 && req->flags & MDP_FLIP_LR)) | ||
119 | rotate_dst_addr_x(req, regs); | ||
120 | if (req->flags & MDP_ROT_90) | ||
121 | regs->op |= PPP_OP_ROT_90; | ||
122 | if (req->flags & MDP_FLIP_UD) { | ||
123 | regs->op |= PPP_OP_FLIP_UD; | ||
124 | rotate_dst_addr_y(req, regs); | ||
125 | } | ||
126 | if (req->flags & MDP_FLIP_LR) | ||
127 | regs->op |= PPP_OP_FLIP_LR; | ||
128 | } | ||
129 | |||
130 | static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs) | ||
131 | { | ||
132 | if (req->src.format == req->dst.format) | ||
133 | return; | ||
134 | if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) { | ||
135 | regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON; | ||
136 | } else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) { | ||
137 | regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON; | ||
138 | if (req->dst.format == MDP_RGB_565) | ||
139 | regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | #define GET_BIT_RANGE(value, high, low) \ | ||
144 | (((1 << (high - low + 1)) - 1) & (value >> low)) | ||
145 | static uint32_t transp_convert(struct mdp_blit_req *req) | ||
146 | { | ||
147 | uint32_t transp = 0; | ||
148 | if (req->src.format == MDP_RGB_565) { | ||
149 | /* pad each value to 8 bits by copying the high bits into the | ||
150 | * low end, convert RGB to RBG by switching low 2 components */ | ||
151 | transp |= ((GET_BIT_RANGE(req->transp_mask, 15, 11) << 3) | | ||
152 | (GET_BIT_RANGE(req->transp_mask, 15, 13))) << 16; | ||
153 | |||
154 | transp |= ((GET_BIT_RANGE(req->transp_mask, 4, 0) << 3) | | ||
155 | (GET_BIT_RANGE(req->transp_mask, 4, 2))) << 8; | ||
156 | |||
157 | transp |= (GET_BIT_RANGE(req->transp_mask, 10, 5) << 2) | | ||
158 | (GET_BIT_RANGE(req->transp_mask, 10, 9)); | ||
159 | } else { | ||
160 | /* convert RGB to RBG */ | ||
161 | transp |= (GET_BIT_RANGE(req->transp_mask, 15, 8)) | | ||
162 | (GET_BIT_RANGE(req->transp_mask, 23, 16) << 16) | | ||
163 | (GET_BIT_RANGE(req->transp_mask, 7, 0) << 8); | ||
164 | } | ||
165 | return transp; | ||
166 | } | ||
167 | #undef GET_BIT_RANGE | ||
168 | |||
169 | static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs) | ||
170 | { | ||
171 | /* TRANSP BLEND */ | ||
172 | if (req->transp_mask != MDP_TRANSP_NOP) { | ||
173 | req->transp_mask = transp_convert(req); | ||
174 | if (req->alpha != MDP_ALPHA_NOP) { | ||
175 | /* use blended transparancy mode | ||
176 | * pixel = (src == transp) ? dst : blend | ||
177 | * blend is combo of blend_eq_sel and | ||
178 | * blend_alpha_sel */ | ||
179 | regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | | ||
180 | PPP_OP_BLEND_ALPHA_BLEND_NORMAL | | ||
181 | PPP_OP_BLEND_CONSTANT_ALPHA | | ||
182 | PPP_BLEND_ALPHA_TRANSP; | ||
183 | } else { | ||
184 | /* simple transparancy mode | ||
185 | * pixel = (src == transp) ? dst : src */ | ||
186 | regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | | ||
187 | PPP_OP_BLEND_SRCPIXEL_TRANSP; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | req->alpha &= 0xff; | ||
192 | /* ALPHA BLEND */ | ||
193 | if (HAS_ALPHA(req->src.format)) { | ||
194 | regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | | ||
195 | PPP_OP_BLEND_SRCPIXEL_ALPHA; | ||
196 | } else if (req->alpha < MDP_ALPHA_NOP) { | ||
197 | /* just blend by alpha */ | ||
198 | regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | | ||
199 | PPP_OP_BLEND_ALPHA_BLEND_NORMAL | | ||
200 | PPP_OP_BLEND_CONSTANT_ALPHA; | ||
201 | } | ||
202 | |||
203 | regs->op |= bg_op_chroma[req->dst.format]; | ||
204 | } | ||
205 | |||
206 | #define ONE_HALF (1LL << 32) | ||
207 | #define ONE (1LL << 33) | ||
208 | #define TWO (2LL << 33) | ||
209 | #define THREE (3LL << 33) | ||
210 | #define FRAC_MASK (ONE - 1) | ||
211 | #define INT_MASK (~FRAC_MASK) | ||
212 | |||
213 | static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, | ||
214 | uint32_t *phase_init, uint32_t *phase_step) | ||
215 | { | ||
216 | /* to improve precicsion calculations are done in U31.33 and converted | ||
217 | * to U3.29 at the end */ | ||
218 | int64_t k1, k2, k3, k4, tmp; | ||
219 | uint64_t n, d, os, os_p, od, od_p, oreq; | ||
220 | unsigned rpa = 0; | ||
221 | int64_t ip64, delta; | ||
222 | |||
223 | if (dim_out % 3 == 0) | ||
224 | rpa = !(dim_in % (dim_out / 3)); | ||
225 | |||
226 | n = ((uint64_t)dim_out) << 34; | ||
227 | d = dim_in; | ||
228 | if (!d) | ||
229 | return -1; | ||
230 | do_div(n, d); | ||
231 | k3 = (n + 1) >> 1; | ||
232 | if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) { | ||
233 | DLOG("crap bad scale\n"); | ||
234 | return -1; | ||
235 | } | ||
236 | n = ((uint64_t)dim_in) << 34; | ||
237 | d = (uint64_t)dim_out; | ||
238 | if (!d) | ||
239 | return -1; | ||
240 | do_div(n, d); | ||
241 | k1 = (n + 1) >> 1; | ||
242 | k2 = (k1 - ONE) >> 1; | ||
243 | |||
244 | *phase_init = (int)(k2 >> 4); | ||
245 | k4 = (k3 - ONE) >> 1; | ||
246 | |||
247 | if (rpa) { | ||
248 | os = ((uint64_t)origin << 33) - ONE_HALF; | ||
249 | tmp = (dim_out * os) + ONE_HALF; | ||
250 | if (!dim_in) | ||
251 | return -1; | ||
252 | do_div(tmp, dim_in); | ||
253 | od = tmp - ONE_HALF; | ||
254 | } else { | ||
255 | os = ((uint64_t)origin << 1) - 1; | ||
256 | od = (((k3 * os) >> 1) + k4); | ||
257 | } | ||
258 | |||
259 | od_p = od & INT_MASK; | ||
260 | if (od_p != od) | ||
261 | od_p += ONE; | ||
262 | |||
263 | if (rpa) { | ||
264 | tmp = (dim_in * od_p) + ONE_HALF; | ||
265 | if (!dim_in) | ||
266 | return -1; | ||
267 | do_div(tmp, dim_in); | ||
268 | os_p = tmp - ONE_HALF; | ||
269 | } else { | ||
270 | os_p = ((k1 * (od_p >> 33)) + k2); | ||
271 | } | ||
272 | |||
273 | oreq = (os_p & INT_MASK) - ONE; | ||
274 | |||
275 | ip64 = os_p - oreq; | ||
276 | delta = ((int64_t)(origin) << 33) - oreq; | ||
277 | ip64 -= delta; | ||
278 | /* limit to valid range before the left shift */ | ||
279 | delta = (ip64 & (1LL << 63)) ? 4 : -4; | ||
280 | delta <<= 33; | ||
281 | while (abs((int)(ip64 >> 33)) > 4) | ||
282 | ip64 += delta; | ||
283 | *phase_init = (int)(ip64 >> 4); | ||
284 | *phase_step = (uint32_t)(k1 >> 4); | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static void load_scale_table(const struct mdp_info *mdp, | ||
289 | struct mdp_table_entry *table, int len) | ||
290 | { | ||
291 | int i; | ||
292 | for (i = 0; i < len; i++) | ||
293 | mdp_writel(mdp, table[i].val, table[i].reg); | ||
294 | } | ||
295 | |||
296 | enum { | ||
297 | IMG_LEFT, | ||
298 | IMG_RIGHT, | ||
299 | IMG_TOP, | ||
300 | IMG_BOTTOM, | ||
301 | }; | ||
302 | |||
303 | static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst, | ||
304 | uint32_t *interp1, uint32_t *interp2, | ||
305 | uint32_t *repeat1, uint32_t *repeat2) { | ||
306 | if (src > 3 * dst) { | ||
307 | *interp1 = 0; | ||
308 | *interp2 = src - 1; | ||
309 | *repeat1 = 0; | ||
310 | *repeat2 = 0; | ||
311 | } else if (src == 3 * dst) { | ||
312 | *interp1 = 0; | ||
313 | *interp2 = src; | ||
314 | *repeat1 = 0; | ||
315 | *repeat2 = 1; | ||
316 | } else if (src > dst && src < 3 * dst) { | ||
317 | *interp1 = -1; | ||
318 | *interp2 = src; | ||
319 | *repeat1 = 1; | ||
320 | *repeat2 = 1; | ||
321 | } else if (src == dst) { | ||
322 | *interp1 = -1; | ||
323 | *interp2 = src + 1; | ||
324 | *repeat1 = 1; | ||
325 | *repeat2 = 2; | ||
326 | } else { | ||
327 | *interp1 = -2; | ||
328 | *interp2 = src + 1; | ||
329 | *repeat1 = 2; | ||
330 | *repeat2 = 2; | ||
331 | } | ||
332 | *interp1 += src_coord; | ||
333 | *interp2 += src_coord; | ||
334 | } | ||
335 | |||
336 | static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs) | ||
337 | { | ||
338 | int32_t luma_interp[4]; | ||
339 | int32_t luma_repeat[4]; | ||
340 | int32_t chroma_interp[4]; | ||
341 | int32_t chroma_bound[4]; | ||
342 | int32_t chroma_repeat[4]; | ||
343 | uint32_t dst_w, dst_h; | ||
344 | |||
345 | memset(&luma_interp, 0, sizeof(int32_t) * 4); | ||
346 | memset(&luma_repeat, 0, sizeof(int32_t) * 4); | ||
347 | memset(&chroma_interp, 0, sizeof(int32_t) * 4); | ||
348 | memset(&chroma_bound, 0, sizeof(int32_t) * 4); | ||
349 | memset(&chroma_repeat, 0, sizeof(int32_t) * 4); | ||
350 | regs->edge = 0; | ||
351 | |||
352 | if (req->flags & MDP_ROT_90) { | ||
353 | dst_w = req->dst_rect.h; | ||
354 | dst_h = req->dst_rect.w; | ||
355 | } else { | ||
356 | dst_w = req->dst_rect.w; | ||
357 | dst_h = req->dst_rect.h; | ||
358 | } | ||
359 | |||
360 | if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) { | ||
361 | get_edge_info(req->src_rect.h, req->src_rect.y, dst_h, | ||
362 | &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM], | ||
363 | &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]); | ||
364 | get_edge_info(req->src_rect.w, req->src_rect.x, dst_w, | ||
365 | &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT], | ||
366 | &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]); | ||
367 | } else { | ||
368 | luma_interp[IMG_LEFT] = req->src_rect.x; | ||
369 | luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; | ||
370 | luma_interp[IMG_TOP] = req->src_rect.y; | ||
371 | luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; | ||
372 | luma_repeat[IMG_LEFT] = 0; | ||
373 | luma_repeat[IMG_TOP] = 0; | ||
374 | luma_repeat[IMG_RIGHT] = 0; | ||
375 | luma_repeat[IMG_BOTTOM] = 0; | ||
376 | } | ||
377 | |||
378 | chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT]; | ||
379 | chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT]; | ||
380 | chroma_interp[IMG_TOP] = luma_interp[IMG_TOP]; | ||
381 | chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM]; | ||
382 | |||
383 | chroma_bound[IMG_LEFT] = req->src_rect.x; | ||
384 | chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; | ||
385 | chroma_bound[IMG_TOP] = req->src_rect.y; | ||
386 | chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; | ||
387 | |||
388 | if (IS_YCRCB(req->src.format)) { | ||
389 | chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1; | ||
390 | chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1; | ||
391 | |||
392 | chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1; | ||
393 | chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1; | ||
394 | } | ||
395 | |||
396 | if (req->src.format == MDP_Y_CBCR_H2V2 || | ||
397 | req->src.format == MDP_Y_CRCB_H2V2) { | ||
398 | chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1; | ||
399 | chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1) | ||
400 | >> 1; | ||
401 | chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1; | ||
402 | chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1; | ||
403 | } | ||
404 | |||
405 | chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] - | ||
406 | chroma_interp[IMG_LEFT]; | ||
407 | chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] - | ||
408 | chroma_bound[IMG_RIGHT]; | ||
409 | chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] - | ||
410 | chroma_interp[IMG_TOP]; | ||
411 | chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] - | ||
412 | chroma_bound[IMG_BOTTOM]; | ||
413 | |||
414 | if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 || | ||
415 | chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 || | ||
416 | chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 || | ||
417 | chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 || | ||
418 | luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 || | ||
419 | luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 || | ||
420 | luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 || | ||
421 | luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3) | ||
422 | return -1; | ||
423 | |||
424 | regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA; | ||
425 | regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA; | ||
426 | regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA; | ||
427 | regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA; | ||
428 | regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA; | ||
429 | regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA; | ||
430 | regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA; | ||
431 | regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA; | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, | ||
436 | struct mdp_regs *regs) | ||
437 | { | ||
438 | uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; | ||
439 | uint32_t scale_factor_x, scale_factor_y; | ||
440 | uint32_t downscale; | ||
441 | uint32_t dst_w, dst_h; | ||
442 | |||
443 | if (req->flags & MDP_ROT_90) { | ||
444 | dst_w = req->dst_rect.h; | ||
445 | dst_h = req->dst_rect.w; | ||
446 | } else { | ||
447 | dst_w = req->dst_rect.w; | ||
448 | dst_h = req->dst_rect.h; | ||
449 | } | ||
450 | if ((req->src_rect.w == dst_w) && (req->src_rect.h == dst_h) && | ||
451 | !(req->flags & MDP_BLUR)) { | ||
452 | regs->phasex_init = 0; | ||
453 | regs->phasey_init = 0; | ||
454 | regs->phasex_step = 0; | ||
455 | regs->phasey_step = 0; | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x, | ||
460 | &phase_step_x) || | ||
461 | scale_params(req->src_rect.h, dst_h, 1, &phase_init_y, | ||
462 | &phase_step_y)) | ||
463 | return -1; | ||
464 | |||
465 | scale_factor_x = (dst_w * 10) / req->src_rect.w; | ||
466 | scale_factor_y = (dst_h * 10) / req->src_rect.h; | ||
467 | |||
468 | if (scale_factor_x > 8) | ||
469 | downscale = MDP_DOWNSCALE_PT8TO1; | ||
470 | else if (scale_factor_x > 6) | ||
471 | downscale = MDP_DOWNSCALE_PT6TOPT8; | ||
472 | else if (scale_factor_x > 4) | ||
473 | downscale = MDP_DOWNSCALE_PT4TOPT6; | ||
474 | else | ||
475 | downscale = MDP_DOWNSCALE_PT2TOPT4; | ||
476 | if (downscale != downscale_x_table) { | ||
477 | load_scale_table(mdp, mdp_downscale_x_table[downscale], 64); | ||
478 | downscale_x_table = downscale; | ||
479 | } | ||
480 | |||
481 | if (scale_factor_y > 8) | ||
482 | downscale = MDP_DOWNSCALE_PT8TO1; | ||
483 | else if (scale_factor_y > 6) | ||
484 | downscale = MDP_DOWNSCALE_PT6TOPT8; | ||
485 | else if (scale_factor_y > 4) | ||
486 | downscale = MDP_DOWNSCALE_PT4TOPT6; | ||
487 | else | ||
488 | downscale = MDP_DOWNSCALE_PT2TOPT4; | ||
489 | if (downscale != downscale_y_table) { | ||
490 | load_scale_table(mdp, mdp_downscale_y_table[downscale], 64); | ||
491 | downscale_y_table = downscale; | ||
492 | } | ||
493 | |||
494 | regs->phasex_init = phase_init_x; | ||
495 | regs->phasey_init = phase_init_y; | ||
496 | regs->phasex_step = phase_step_x; | ||
497 | regs->phasey_step = phase_step_y; | ||
498 | regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); | ||
499 | return 0; | ||
500 | |||
501 | } | ||
502 | |||
503 | static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req, | ||
504 | struct mdp_regs *regs) | ||
505 | { | ||
506 | if (!(req->flags & MDP_BLUR)) | ||
507 | return; | ||
508 | |||
509 | if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && | ||
510 | downscale_y_table == MDP_DOWNSCALE_BLUR)) { | ||
511 | load_scale_table(mdp, mdp_gaussian_blur_table, 128); | ||
512 | downscale_x_table = MDP_DOWNSCALE_BLUR; | ||
513 | downscale_y_table = MDP_DOWNSCALE_BLUR; | ||
514 | } | ||
515 | |||
516 | regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); | ||
517 | } | ||
518 | |||
519 | |||
520 | #define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp) | ||
521 | |||
522 | #define Y_TO_CRCB_RATIO(format) \ | ||
523 | ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\ | ||
524 | (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1) | ||
525 | |||
526 | static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, | ||
527 | uint32_t *len0, uint32_t *len1) | ||
528 | { | ||
529 | *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp); | ||
530 | if (IS_PSEUDOPLNR(img->format)) | ||
531 | *len1 = *len0/Y_TO_CRCB_RATIO(img->format); | ||
532 | else | ||
533 | *len1 = 0; | ||
534 | } | ||
535 | |||
536 | static int valid_src_dst(unsigned long src_start, unsigned long src_len, | ||
537 | unsigned long dst_start, unsigned long dst_len, | ||
538 | struct mdp_blit_req *req, struct mdp_regs *regs) | ||
539 | { | ||
540 | unsigned long src_min_ok = src_start; | ||
541 | unsigned long src_max_ok = src_start + src_len; | ||
542 | unsigned long dst_min_ok = dst_start; | ||
543 | unsigned long dst_max_ok = dst_start + dst_len; | ||
544 | uint32_t src0_len, src1_len, dst0_len, dst1_len; | ||
545 | get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len, | ||
546 | &src1_len); | ||
547 | get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len, | ||
548 | &dst1_len); | ||
549 | |||
550 | if (regs->src0 < src_min_ok || regs->src0 > src_max_ok || | ||
551 | regs->src0 + src0_len > src_max_ok) { | ||
552 | DLOG("invalid_src %x %x %lx %lx\n", regs->src0, | ||
553 | src0_len, src_min_ok, src_max_ok); | ||
554 | return 0; | ||
555 | } | ||
556 | if (regs->src_cfg & PPP_SRC_PLANE_PSEUDOPLNR) { | ||
557 | if (regs->src1 < src_min_ok || regs->src1 > src_max_ok || | ||
558 | regs->src1 + src1_len > src_max_ok) { | ||
559 | DLOG("invalid_src1"); | ||
560 | return 0; | ||
561 | } | ||
562 | } | ||
563 | if (regs->dst0 < dst_min_ok || regs->dst0 > dst_max_ok || | ||
564 | regs->dst0 + dst0_len > dst_max_ok) { | ||
565 | DLOG("invalid_dst"); | ||
566 | return 0; | ||
567 | } | ||
568 | if (regs->dst_cfg & PPP_SRC_PLANE_PSEUDOPLNR) { | ||
569 | if (regs->dst1 < dst_min_ok || regs->dst1 > dst_max_ok || | ||
570 | regs->dst1 + dst1_len > dst_max_ok) { | ||
571 | DLOG("invalid_dst1"); | ||
572 | return 0; | ||
573 | } | ||
574 | } | ||
575 | return 1; | ||
576 | } | ||
577 | |||
578 | |||
579 | static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs, | ||
580 | struct file *src_file, struct file *dst_file) | ||
581 | { | ||
582 | } | ||
583 | |||
584 | static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect, | ||
585 | uint32_t base, uint32_t bpp, uint32_t cfg, | ||
586 | uint32_t *addr, uint32_t *ystride) | ||
587 | { | ||
588 | uint32_t compress_v = Y_TO_CRCB_RATIO(img->format); | ||
589 | uint32_t compress_h = 2; | ||
590 | uint32_t offset; | ||
591 | |||
592 | if (IS_PSEUDOPLNR(img->format)) { | ||
593 | offset = (rect->x / compress_h) * compress_h; | ||
594 | offset += rect->y == 0 ? 0 : | ||
595 | ((rect->y + 1) / compress_v) * img->width; | ||
596 | *addr = base + (img->width * img->height * bpp); | ||
597 | *addr += offset * bpp; | ||
598 | *ystride |= *ystride << 16; | ||
599 | } else { | ||
600 | *addr = 0; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, | ||
605 | struct mdp_regs *regs, struct file *src_file, | ||
606 | struct file *dst_file) | ||
607 | { | ||
608 | mdp_writel(mdp, 1, 0x060); | ||
609 | mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI); | ||
610 | mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0); | ||
611 | mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1); | ||
612 | mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE); | ||
613 | mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG); | ||
614 | mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN); | ||
615 | |||
616 | mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION); | ||
617 | mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT); | ||
618 | mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT); | ||
619 | mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP); | ||
620 | mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP); | ||
621 | |||
622 | mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff), | ||
623 | PPP_ADDR_ALPHA_TRANSP); | ||
624 | |||
625 | mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG); | ||
626 | mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN); | ||
627 | mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI); | ||
628 | mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0); | ||
629 | mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1); | ||
630 | mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE); | ||
631 | |||
632 | mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE); | ||
633 | if (regs->op & PPP_OP_BLEND_ON) { | ||
634 | mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0); | ||
635 | mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1); | ||
636 | mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE); | ||
637 | mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG); | ||
638 | mdp_writel(mdp, pack_pattern[req->dst.format], | ||
639 | PPP_ADDR_BG_PACK_PATTERN); | ||
640 | } | ||
641 | flush_imgs(req, regs, src_file, dst_file); | ||
642 | mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START); | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, | ||
647 | struct file *src_file, unsigned long src_start, unsigned long src_len, | ||
648 | struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||
649 | { | ||
650 | struct mdp_regs regs = {0}; | ||
651 | |||
652 | if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT || | ||
653 | req->dst.format >= MDP_IMGTYPE_LIMIT)) { | ||
654 | printk(KERN_ERR "mpd_ppp: img is of wrong format\n"); | ||
655 | return -EINVAL; | ||
656 | } | ||
657 | |||
658 | if (unlikely(req->src_rect.x > req->src.width || | ||
659 | req->src_rect.y > req->src.height || | ||
660 | req->dst_rect.x > req->dst.width || | ||
661 | req->dst_rect.y > req->dst.height)) { | ||
662 | printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n"); | ||
663 | return -EINVAL; | ||
664 | } | ||
665 | |||
666 | /* set the src image configuration */ | ||
667 | regs.src_cfg = src_img_cfg[req->src.format]; | ||
668 | regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0; | ||
669 | regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0; | ||
670 | regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w; | ||
671 | regs.src_pack = pack_pattern[req->src.format]; | ||
672 | |||
673 | /* set the dest image configuration */ | ||
674 | regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI; | ||
675 | regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w; | ||
676 | regs.dst_pack = pack_pattern[req->dst.format]; | ||
677 | |||
678 | /* set src, bpp, start pixel and ystride */ | ||
679 | regs.src_bpp = bytes_per_pixel[req->src.format]; | ||
680 | regs.src0 = src_start + req->src.offset; | ||
681 | regs.src_ystride = req->src.width * regs.src_bpp; | ||
682 | get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp, | ||
683 | regs.src_cfg, ®s.src1, ®s.src_ystride); | ||
684 | regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) * | ||
685 | regs.src_bpp; | ||
686 | |||
687 | /* set dst, bpp, start pixel and ystride */ | ||
688 | regs.dst_bpp = bytes_per_pixel[req->dst.format]; | ||
689 | regs.dst0 = dst_start + req->dst.offset; | ||
690 | regs.dst_ystride = req->dst.width * regs.dst_bpp; | ||
691 | get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp, | ||
692 | regs.dst_cfg, ®s.dst1, ®s.dst_ystride); | ||
693 | regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) * | ||
694 | regs.dst_bpp; | ||
695 | |||
696 | if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req, | ||
697 | ®s)) { | ||
698 | printk(KERN_ERR "mpd_ppp: final src or dst location is " | ||
699 | "invalid, are you trying to make an image too large " | ||
700 | "or to place it outside the screen?\n"); | ||
701 | return -EINVAL; | ||
702 | } | ||
703 | |||
704 | /* set up operation register */ | ||
705 | regs.op = 0; | ||
706 | blit_rotate(req, ®s); | ||
707 | blit_convert(req, ®s); | ||
708 | if (req->flags & MDP_DITHER) | ||
709 | regs.op |= PPP_OP_DITHER_EN; | ||
710 | blit_blend(req, ®s); | ||
711 | if (blit_scale(mdp, req, ®s)) { | ||
712 | printk(KERN_ERR "mpd_ppp: error computing scale for img.\n"); | ||
713 | return -EINVAL; | ||
714 | } | ||
715 | blit_blur(mdp, req, ®s); | ||
716 | regs.op |= dst_op_chroma[req->dst.format] | | ||
717 | src_op_chroma[req->src.format]; | ||
718 | |||
719 | /* if the image is YCRYCB, the x and w must be even */ | ||
720 | if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) { | ||
721 | req->src_rect.x = req->src_rect.x & (~0x1); | ||
722 | req->src_rect.w = req->src_rect.w & (~0x1); | ||
723 | req->dst_rect.x = req->dst_rect.x & (~0x1); | ||
724 | req->dst_rect.w = req->dst_rect.w & (~0x1); | ||
725 | } | ||
726 | if (get_edge_cond(req, ®s)) | ||
727 | return -EINVAL; | ||
728 | |||
729 | send_blit(mdp, req, ®s, src_file, dst_file); | ||
730 | return 0; | ||
731 | } | ||