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