aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/msm/mdp_ppp.c
diff options
context:
space:
mode:
authorPavel Machek <pavel@ucw.cz>2009-09-22 19:47:03 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:50 -0400
commitd480ace08d5b59133575e672a0bd1c97b0f8400f (patch)
tree3e2e0edd582d7f511544ad87a6095c6227d280aa /drivers/video/msm/mdp_ppp.c
parent689620100172e24fdf0981e9978a9559e8769258 (diff)
fbdev: framebuffer support for HTC Dream
Add a framebuffer driver for Qualcomm MSM/QSD SoCs, tested on HTC Dream smartphone (aka T-Mobile G1, aka ADP1). Brian said: I did the original quick and dirty version for bringup. Rebecca took over and (re)wrote the bulk of the driver, getting things stable for production ship of Dream and Sapphire, and Dima is currently adding support for later Qualcomm chipsets (QSD8x50, etc). Signed-off-by: Pavel Machek <pavel@ucw.cz> Cc: Brian Swetland <swetland@google.com> Cc: Krzysztof Helt <krzysztof.h1@poczta.fm> Cc: Rebecca Schultz Zavin <rebecca@android.com> Cc: Dima Zavin <dima@android.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/msm/mdp_ppp.c')
-rw-r--r--drivers/video/msm/mdp_ppp.c750
1 files changed, 750 insertions, 0 deletions
diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c
new file mode 100644
index 000000000000..ba2c4673b648
--- /dev/null
+++ b/drivers/video/msm/mdp_ppp.c
@@ -0,0 +1,750 @@
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/android_pmem.h>
20#include <mach/msm_fb.h>
21
22#include "mdp_hw.h"
23#include "mdp_scale_tables.h"
24
25#define DLOG(x...) do {} while (0)
26
27#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1)
28static int downscale_y_table = MDP_DOWNSCALE_MAX;
29static int downscale_x_table = MDP_DOWNSCALE_MAX;
30
31struct mdp_regs {
32 uint32_t src0;
33 uint32_t src1;
34 uint32_t dst0;
35 uint32_t dst1;
36 uint32_t src_cfg;
37 uint32_t dst_cfg;
38 uint32_t src_pack;
39 uint32_t dst_pack;
40 uint32_t src_rect;
41 uint32_t dst_rect;
42 uint32_t src_ystride;
43 uint32_t dst_ystride;
44 uint32_t op;
45 uint32_t src_bpp;
46 uint32_t dst_bpp;
47 uint32_t edge;
48 uint32_t phasex_init;
49 uint32_t phasey_init;
50 uint32_t phasex_step;
51 uint32_t phasey_step;
52};
53
54static uint32_t pack_pattern[] = {
55 PPP_ARRAY0(PACK_PATTERN)
56};
57
58static uint32_t src_img_cfg[] = {
59 PPP_ARRAY1(CFG, SRC)
60};
61
62static uint32_t dst_img_cfg[] = {
63 PPP_ARRAY1(CFG, DST)
64};
65
66static uint32_t bytes_per_pixel[] = {
67 [MDP_RGB_565] = 2,
68 [MDP_RGB_888] = 3,
69 [MDP_XRGB_8888] = 4,
70 [MDP_ARGB_8888] = 4,
71 [MDP_RGBA_8888] = 4,
72 [MDP_BGRA_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
80static uint32_t dst_op_chroma[] = {
81 PPP_ARRAY1(CHROMA_SAMP, DST)
82};
83
84static uint32_t src_op_chroma[] = {
85 PPP_ARRAY1(CHROMA_SAMP, SRC)
86};
87
88static uint32_t bg_op_chroma[] = {
89 PPP_ARRAY1(CHROMA_SAMP, BG)
90};
91
92static 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
100static 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
110static 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
130static 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))
145static 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
169static 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
213static 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
288static 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
296enum {
297IMG_LEFT,
298IMG_RIGHT,
299IMG_TOP,
300IMG_BOTTOM,
301};
302
303static 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
336static 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
435static 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
503static 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
526static 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
536static 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
579static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs,
580 struct file *src_file, struct file *dst_file)
581{
582#ifdef CONFIG_ANDROID_PMEM
583 uint32_t src0_len, src1_len, dst0_len, dst1_len;
584
585 /* flush src images to memory before dma to mdp */
586 get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len,
587 &src1_len);
588 flush_pmem_file(src_file, req->src.offset, src0_len);
589 if (IS_PSEUDOPLNR(req->src.format))
590 flush_pmem_file(src_file, req->src.offset + src0_len,
591 src1_len);
592
593 /* flush dst images */
594 get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len,
595 &dst1_len);
596 flush_pmem_file(dst_file, req->dst.offset, dst0_len);
597 if (IS_PSEUDOPLNR(req->dst.format))
598 flush_pmem_file(dst_file, req->dst.offset + dst0_len,
599 dst1_len);
600#endif
601}
602
603static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect,
604 uint32_t base, uint32_t bpp, uint32_t cfg,
605 uint32_t *addr, uint32_t *ystride)
606{
607 uint32_t compress_v = Y_TO_CRCB_RATIO(img->format);
608 uint32_t compress_h = 2;
609 uint32_t offset;
610
611 if (IS_PSEUDOPLNR(img->format)) {
612 offset = (rect->x / compress_h) * compress_h;
613 offset += rect->y == 0 ? 0 :
614 ((rect->y + 1) / compress_v) * img->width;
615 *addr = base + (img->width * img->height * bpp);
616 *addr += offset * bpp;
617 *ystride |= *ystride << 16;
618 } else {
619 *addr = 0;
620 }
621}
622
623static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
624 struct mdp_regs *regs, struct file *src_file,
625 struct file *dst_file)
626{
627 mdp_writel(mdp, 1, 0x060);
628 mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI);
629 mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0);
630 mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1);
631 mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE);
632 mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG);
633 mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN);
634
635 mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION);
636 mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT);
637 mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT);
638 mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP);
639 mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP);
640
641 mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff),
642 PPP_ADDR_ALPHA_TRANSP);
643
644 mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG);
645 mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN);
646 mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI);
647 mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0);
648 mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1);
649 mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE);
650
651 mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE);
652 if (regs->op & PPP_OP_BLEND_ON) {
653 mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0);
654 mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1);
655 mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE);
656 mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG);
657 mdp_writel(mdp, pack_pattern[req->dst.format],
658 PPP_ADDR_BG_PACK_PATTERN);
659 }
660 flush_imgs(req, regs, src_file, dst_file);
661 mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START);
662 return 0;
663}
664
665int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
666 struct file *src_file, unsigned long src_start, unsigned long src_len,
667 struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
668{
669 struct mdp_regs regs = {0};
670
671 if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT ||
672 req->dst.format >= MDP_IMGTYPE_LIMIT)) {
673 printk(KERN_ERR "mpd_ppp: img is of wrong format\n");
674 return -EINVAL;
675 }
676
677 if (unlikely(req->src_rect.x > req->src.width ||
678 req->src_rect.y > req->src.height ||
679 req->dst_rect.x > req->dst.width ||
680 req->dst_rect.y > req->dst.height)) {
681 printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n");
682 return -EINVAL;
683 }
684
685 /* set the src image configuration */
686 regs.src_cfg = src_img_cfg[req->src.format];
687 regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0;
688 regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
689 regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w;
690 regs.src_pack = pack_pattern[req->src.format];
691
692 /* set the dest image configuration */
693 regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI;
694 regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w;
695 regs.dst_pack = pack_pattern[req->dst.format];
696
697 /* set src, bpp, start pixel and ystride */
698 regs.src_bpp = bytes_per_pixel[req->src.format];
699 regs.src0 = src_start + req->src.offset;
700 regs.src_ystride = req->src.width * regs.src_bpp;
701 get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp,
702 regs.src_cfg, &regs.src1, &regs.src_ystride);
703 regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) *
704 regs.src_bpp;
705
706 /* set dst, bpp, start pixel and ystride */
707 regs.dst_bpp = bytes_per_pixel[req->dst.format];
708 regs.dst0 = dst_start + req->dst.offset;
709 regs.dst_ystride = req->dst.width * regs.dst_bpp;
710 get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp,
711 regs.dst_cfg, &regs.dst1, &regs.dst_ystride);
712 regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) *
713 regs.dst_bpp;
714
715 if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req,
716 &regs)) {
717 printk(KERN_ERR "mpd_ppp: final src or dst location is "
718 "invalid, are you trying to make an image too large "
719 "or to place it outside the screen?\n");
720 return -EINVAL;
721 }
722
723 /* set up operation register */
724 regs.op = 0;
725 blit_rotate(req, &regs);
726 blit_convert(req, &regs);
727 if (req->flags & MDP_DITHER)
728 regs.op |= PPP_OP_DITHER_EN;
729 blit_blend(req, &regs);
730 if (blit_scale(mdp, req, &regs)) {
731 printk(KERN_ERR "mpd_ppp: error computing scale for img.\n");
732 return -EINVAL;
733 }
734 blit_blur(mdp, req, &regs);
735 regs.op |= dst_op_chroma[req->dst.format] |
736 src_op_chroma[req->src.format];
737
738 /* if the image is YCRYCB, the x and w must be even */
739 if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) {
740 req->src_rect.x = req->src_rect.x & (~0x1);
741 req->src_rect.w = req->src_rect.w & (~0x1);
742 req->dst_rect.x = req->dst_rect.x & (~0x1);
743 req->dst_rect.w = req->dst_rect.w & (~0x1);
744 }
745 if (get_edge_cond(req, &regs))
746 return -EINVAL;
747
748 send_blit(mdp, req, &regs, src_file, dst_file);
749 return 0;
750}