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> | |
1ef21f63 | 19 | #include <linux/platform_data/video-msm_fb.h> |
d480ace0 PM |
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, | |
a8d380f3 | 72 | [MDP_RGBX_8888] = 4, |
d480ace0 PM |
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 | { | |
d480ace0 PM |
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 | } |