/* drivers/video/msm/mdp_ppp31.c * * Copyright (C) 2009 QUALCOMM Incorporated * Copyright (C) 2009 Google Incorporated * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include "mdp_hw.h" #include "mdp_ppp.h" #define NUM_COEFFS 32 struct mdp_scale_coeffs { uint16_t c[4][NUM_COEFFS]; }; struct mdp_scale_tbl_info { uint16_t offset; uint32_t set:2; int use_pr; struct mdp_scale_coeffs coeffs; }; enum { MDP_SCALE_PT2TOPT4, MDP_SCALE_PT4TOPT6, MDP_SCALE_PT6TOPT8, MDP_SCALE_PT8TO8, MDP_SCALE_MAX, }; static struct mdp_scale_coeffs mdp_scale_pr_coeffs = { .c = { [0] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [1] = { 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [2] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, }, [3] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, }, }; static struct mdp_scale_tbl_info mdp_scale_tbl[MDP_SCALE_MAX] = { [ MDP_SCALE_PT2TOPT4 ] = { .offset = 0, .set = MDP_PPP_SCALE_COEFF_D0_SET, .use_pr = -1, .coeffs.c = { [0] = { 131, 131, 130, 129, 128, 127, 127, 126, 125, 125, 124, 123, 123, 121, 120, 119, 119, 118, 117, 117, 116, 115, 115, 114, 113, 112, 111, 110, 109, 109, 108, 107, }, [1] = { 141, 140, 140, 140, 140, 139, 138, 138, 138, 137, 137, 137, 136, 137, 137, 137, 136, 136, 136, 135, 135, 135, 134, 134, 134, 134, 134, 133, 133, 132, 132, 132, }, [2] = { 132, 132, 132, 133, 133, 134, 134, 134, 134, 134, 135, 135, 135, 136, 136, 136, 137, 137, 137, 136, 137, 137, 137, 138, 138, 138, 139, 140, 140, 140, 140, 141, }, [3] = { 107, 108, 109, 109, 110, 111, 112, 113, 114, 115, 115, 116, 117, 117, 118, 119, 119, 120, 121, 123, 123, 124, 125, 125, 126, 127, 127, 128, 129, 130, 131, 131, } }, }, [ MDP_SCALE_PT4TOPT6 ] = { .offset = 32, .set = MDP_PPP_SCALE_COEFF_D1_SET, .use_pr = -1, .coeffs.c = { [0] = { 136, 132, 128, 123, 119, 115, 111, 107, 103, 98, 95, 91, 87, 84, 80, 76, 73, 69, 66, 62, 59, 57, 54, 50, 47, 44, 41, 39, 36, 33, 32, 29, }, [1] = { 206, 205, 204, 204, 201, 200, 199, 197, 196, 194, 191, 191, 189, 185, 184, 182, 180, 178, 176, 173, 170, 168, 165, 162, 160, 157, 155, 152, 148, 146, 142, 140, }, [2] = { 140, 142, 146, 148, 152, 155, 157, 160, 162, 165, 168, 170, 173, 176, 178, 180, 182, 184, 185, 189, 191, 191, 194, 196, 197, 199, 200, 201, 204, 204, 205, 206, }, [3] = { 29, 32, 33, 36, 39, 41, 44, 47, 50, 54, 57, 59, 62, 66, 69, 73, 76, 80, 84, 87, 91, 95, 98, 103, 107, 111, 115, 119, 123, 128, 132, 136, }, }, }, [ MDP_SCALE_PT6TOPT8 ] = { .offset = 64, .set = MDP_PPP_SCALE_COEFF_D2_SET, .use_pr = -1, .coeffs.c = { [0] = { 104, 96, 89, 82, 75, 68, 61, 55, 49, 43, 38, 33, 28, 24, 20, 16, 12, 9, 6, 4, 2, 0, -2, -4, -5, -6, -7, -7, -8, -8, -8, -8, }, [1] = { 303, 303, 302, 300, 298, 296, 293, 289, 286, 281, 276, 270, 265, 258, 252, 245, 238, 230, 223, 214, 206, 197, 189, 180, 172, 163, 154, 145, 137, 128, 120, 112, }, [2] = { 112, 120, 128, 137, 145, 154, 163, 172, 180, 189, 197, 206, 214, 223, 230, 238, 245, 252, 258, 265, 270, 276, 281, 286, 289, 293, 296, 298, 300, 302, 303, 303, }, [3] = { -8, -8, -8, -8, -7, -7, -6, -5, -4, -2, 0, 2, 4, 6, 9, 12, 16, 20, 24, 28, 33, 38, 43, 49, 55, 61, 68, 75, 82, 89, 96, 104, }, }, }, [ MDP_SCALE_PT8TO8 ] = { .offset = 96, .set = MDP_PPP_SCALE_COEFF_U1_SET, .use_pr = -1, .coeffs.c = { [0] = { 0, -7, -13, -19, -24, -28, -32, -34, -37, -39, -40, -41, -41, -41, -40, -40, -38, -37, -35, -33, -31, -29, -26, -24, -21, -18, -15, -13, -10, -7, -5, -2, }, [1] = { 511, 507, 501, 494, 485, 475, 463, 450, 436, 422, 405, 388, 370, 352, 333, 314, 293, 274, 253, 233, 213, 193, 172, 152, 133, 113, 95, 77, 60, 43, 28, 13, }, [2] = { 0, 13, 28, 43, 60, 77, 95, 113, 133, 152, 172, 193, 213, 233, 253, 274, 294, 314, 333, 352, 370, 388, 405, 422, 436, 450, 463, 475, 485, 494, 501, 507, }, [3] = { 0, -2, -5, -7, -10, -13, -15, -18, -21, -24, -26, -29, -31, -33, -35, -37, -38, -40, -40, -41, -41, -41, -40, -39, -37, -34, -32, -28, -24, -19, -13, -7, }, }, }, }; static void load_table(const struct mdp_info *mdp, int scale, int use_pr) { int i; uint32_t val; struct mdp_scale_coeffs *coeffs; struct mdp_scale_tbl_info *tbl = &mdp_scale_tbl[scale]; if (use_pr == tbl->use_pr) return; tbl->use_pr = use_pr; if (!use_pr) coeffs = &tbl->coeffs; else coeffs = &mdp_scale_pr_coeffs; for (i = 0; i < NUM_COEFFS; ++i) { val = ((coeffs->c[1][i] & 0x3ff) << 16) | (coeffs->c[0][i] & 0x3ff); mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_LSBn(tbl->offset + i)); val = ((coeffs->c[3][i] & 0x3ff) << 16) | (coeffs->c[2][i] & 0x3ff); mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_MSBn(tbl->offset + i)); } } #define SCALER_PHASE_BITS 29 static void scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t scaler, uint32_t *phase_init, uint32_t *phase_step) { uint64_t src = dim_in; uint64_t dst = dim_out; uint64_t numer; uint64_t denom; *phase_init = 0; if (dst == 1) { /* if destination is 1 pixel wide, the value of phase_step * is unimportant. */ *phase_step = (uint32_t) (src << SCALER_PHASE_BITS); if (scaler == MDP_PPP_SCALER_FIR) *phase_init = (uint32_t) ((src - 1) << SCALER_PHASE_BITS); return; } if (scaler == MDP_PPP_SCALER_FIR) { numer = (src - 1) << SCALER_PHASE_BITS; denom = dst - 1; /* we want to round up the result*/ numer += denom - 1; } else { numer = src << SCALER_PHASE_BITS; denom = dst; } do_div(numer, denom); *phase_step = (uint32_t) numer; } static int scale_idx(int factor) { int idx; if (factor > 80) idx = MDP_SCALE_PT8TO8; else if (factor > 60) idx = MDP_SCALE_PT6TOPT8; else if (factor > 40) idx = MDP_SCALE_PT4TOPT6; else idx = MDP_SCALE_PT2TOPT4; return idx; } int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs, struct mdp_rect *src_rect, struct mdp_rect *dst_rect, uint32_t src_format, uint32_t dst_format) { uint32_t x_fac; uint32_t y_fac; uint32_t scaler_x = MDP_PPP_SCALER_FIR; uint32_t scaler_y = MDP_PPP_SCALER_FIR; // Don't use pixel repeat mode, it looks bad int use_pr = 0; int x_idx; int y_idx; if (unlikely(src_rect->w > 2048 || src_rect->h > 2048)) return -ENOTSUPP; x_fac = (dst_rect->w * 100) / src_rect->w; y_fac = (dst_rect->h * 100) / src_rect->h; /* if down-scaling by a factor smaller than 1/4, use M/N */ scaler_x = x_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR; scaler_y = y_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR; scale_params(src_rect->w, dst_rect->w, scaler_x, ®s->phasex_init, ®s->phasex_step); scale_params(src_rect->h, dst_rect->h, scaler_y, ®s->phasey_init, ®s->phasey_step); x_idx = scale_idx(x_fac); y_idx = scale_idx(y_fac); load_table(mdp, x_idx, use_pr); load_table(mdp, y_idx, use_pr); regs->scale_cfg = 0; // Enable SVI when source or destination is YUV if (!IS_RGB(src_format) && !IS_RGB(dst_format)) regs->scale_cfg |= (1 << 6); regs->scale_cfg |= (mdp_scale_tbl[x_idx].set << 2) | (mdp_scale_tbl[x_idx].set << 4); regs->scale_cfg |= (scaler_x << 0) | (scaler_y << 1); return 0; } int mdp_ppp_load_blur(struct mdp_info *mdp) { return -ENOTSUPP; } int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) { return 0; } void mdp_ppp_init_scale(struct mdp_info *mdp) { int scale; for (scale = 0; scale < MDP_SCALE_MAX; ++scale) load_table(mdp, scale, 0); } /* Splits a blit into two horizontal stripes. Used to work around MDP bugs */ static int blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { int ret; struct mdp_blit_req splitreq; int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; splitreq = *req; /* break dest roi at height*/ d_x_0 = d_x_1 = req->dst_rect.x; d_w_0 = d_w_1 = req->dst_rect.w; d_y_0 = req->dst_rect.y; if (req->dst_rect.h % 32 == 3) d_h_1 = (req->dst_rect.h - 3) / 2 - 1; else d_h_1 = (req->dst_rect.h - 1) / 2 - 1; d_h_0 = req->dst_rect.h - d_h_1; d_y_1 = d_y_0 + d_h_0; if (req->dst_rect.h == 3) { d_h_1 = 2; d_h_0 = 2; d_y_1 = d_y_0 + 1; } /* break source roi */ if (splitreq.flags & MDP_ROT_90) { s_y_0 = s_y_1 = req->src_rect.y; s_h_0 = s_h_1 = req->src_rect.h; s_x_0 = req->src_rect.x; s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h; s_w_0 = req->src_rect.w - s_w_1; s_x_1 = s_x_0 + s_w_0; if (d_h_1 >= 8 * s_w_1) { s_w_1++; s_x_1--; } } else { s_x_0 = s_x_1 = req->src_rect.x; s_w_0 = s_w_1 = req->src_rect.w; s_y_0 = req->src_rect.y; s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h; s_h_0 = req->src_rect.h - s_h_1; s_y_1 = s_y_0 + s_h_0; if (d_h_1 >= 8 * s_h_1) { s_h_1++; s_y_1--; } } /* blit first region */ if (((splitreq.flags & 0x07) == MDP_ROT_90) || ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_0; splitreq.src_rect.y = s_y_0; splitreq.dst_rect.h = d_h_0; splitreq.dst_rect.y = d_y_0; splitreq.src_rect.x = s_x_0; splitreq.src_rect.w = s_w_0; splitreq.dst_rect.x = d_x_0; splitreq.dst_rect.w = d_w_0; } else { splitreq.src_rect.h = s_h_0; splitreq.src_rect.y = s_y_0; splitreq.dst_rect.h = d_h_1; splitreq.dst_rect.y = d_y_1; splitreq.src_rect.x = s_x_0; splitreq.src_rect.w = s_w_0; splitreq.dst_rect.x = d_x_1; splitreq.dst_rect.w = d_w_1; } ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); if (ret) return ret; /* blit second region */ if (((splitreq.flags & 0x07) == MDP_ROT_90) || ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_1; splitreq.src_rect.y = s_y_1; splitreq.dst_rect.h = d_h_1; splitreq.dst_rect.y = d_y_1; splitreq.src_rect.x = s_x_1; splitreq.src_rect.w = s_w_1; splitreq.dst_rect.x = d_x_1; splitreq.dst_rect.w = d_w_1; } else { splitreq.src_rect.h = s_h_1; splitreq.src_rect.y = s_y_1; splitreq.dst_rect.h = d_h_0; splitreq.dst_rect.y = d_y_0; splitreq.src_rect.x = s_x_1; splitreq.src_rect.w = s_w_1; splitreq.dst_rect.x = d_x_0; splitreq.dst_rect.w = d_w_0; } ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); return ret; } /* Splits a blit into two vertical stripes. Used to work around MDP bugs */ static int blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { int ret; struct mdp_blit_req splitreq; int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; splitreq = *req; /* break dest roi at width*/ d_y_0 = d_y_1 = req->dst_rect.y; d_h_0 = d_h_1 = req->dst_rect.h; d_x_0 = req->dst_rect.x; if (req->dst_rect.w % 32 == 6) d_w_1 = req->dst_rect.w / 2 - 1; else if (req->dst_rect.w % 2 == 0) d_w_1 = req->dst_rect.w / 2; else if (req->dst_rect.w % 32 == 3) d_w_1 = (req->dst_rect.w - 3) / 2 - 1; else d_w_1 = (req->dst_rect.w - 1) / 2 - 1; d_w_0 = req->dst_rect.w - d_w_1; d_x_1 = d_x_0 + d_w_0; if (req->dst_rect.w == 3) { d_w_1 = 2; d_w_0 = 2; d_x_1 = d_x_0 + 1; } /* break src roi at height or width*/ if (splitreq.flags & MDP_ROT_90) { s_x_0 = s_x_1 = req->src_rect.x; s_w_0 = s_w_1 = req->src_rect.w; s_y_0 = req->src_rect.y; s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w; s_h_0 = req->src_rect.h - s_h_1; s_y_1 = s_y_0 + s_h_0; if (d_w_1 >= 8 * s_h_1) { s_h_1++; s_y_1--; } } else { s_y_0 = s_y_1 = req->src_rect.y; s_h_0 = s_h_1 = req->src_rect.h; s_x_0 = req->src_rect.x; s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w; s_w_0 = req->src_rect.w - s_w_1; s_x_1 = s_x_0 + s_w_0; if (d_w_1 >= 8 * s_w_1) { s_w_1++; s_x_1--; } } /* blit first region */ if (((splitreq.flags & 0x07) == MDP_ROT_270) || ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_0; splitreq.src_rect.y = s_y_0; splitreq.dst_rect.h = d_h_0; splitreq.dst_rect.y = d_y_0; splitreq.src_rect.x = s_x_0; splitreq.src_rect.w = s_w_0; splitreq.dst_rect.x = d_x_0; splitreq.dst_rect.w = d_w_0; } else { splitreq.src_rect.h = s_h_0; splitreq.src_rect.y = s_y_0; splitreq.dst_rect.h = d_h_1; splitreq.dst_rect.y = d_y_1; splitreq.src_rect.x = s_x_0; splitreq.src_rect.w = s_w_0; splitreq.dst_rect.x = d_x_1; splitreq.dst_rect.w = d_w_1; } if (unlikely((splitreq.dst_rect.h != 1) && ((splitreq.dst_rect.h % 32 == 3) || (splitreq.dst_rect.h % 32) == 1))) ret = blit_split_height(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); else ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); if (ret) return ret; /* blit second region */ if (((splitreq.flags & 0x07) == MDP_ROT_270) || ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_1; splitreq.src_rect.y = s_y_1; splitreq.dst_rect.h = d_h_1; splitreq.dst_rect.y = d_y_1; splitreq.src_rect.x = s_x_1; splitreq.src_rect.w = s_w_1; splitreq.dst_rect.x = d_x_1; splitreq.dst_rect.w = d_w_1; } else { splitreq.src_rect.h = s_h_1; splitreq.src_rect.y = s_y_1; splitreq.dst_rect.h = d_h_0; splitreq.dst_rect.y = d_y_0; splitreq.src_rect.x = s_x_1; splitreq.src_rect.w = s_w_1; splitreq.dst_rect.x = d_x_0; splitreq.dst_rect.w = d_w_0; } if (unlikely((splitreq.dst_rect.h != 1) && ((splitreq.dst_rect.h % 32 == 3) || (splitreq.dst_rect.h % 32) == 1))) ret = blit_split_height(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); else ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); return ret; } int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req) { if (req->flags & MDP_ROT_90) { if (unlikely(((req->dst_rect.h == 1) && ((req->src_rect.w != 1) || (req->dst_rect.w != req->src_rect.h))) || ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || (req->dst_rect.h != req->src_rect.w))))) { pr_err("mpd_ppp: error scaling when size is 1!\n"); return -EINVAL; } } else { if (unlikely(((req->dst_rect.w == 1) && ((req->src_rect.w != 1) || (req->dst_rect.h != req->src_rect.h))) || ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || (req->dst_rect.h != req->src_rect.h))))) { pr_err("mpd_ppp: error scaling when size is 1!\n"); return -EINVAL; } } /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { pr_info("mdp_ppp: src img of zero size!\n"); return -EINVAL; } if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0)) return -EINVAL; return 0; } int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { int ret; /* Workarounds for MDP 3.1 hardware bugs */ if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) && (req->dst_rect.w != 1) && (((req->dst_rect.w % 8) == 6) || ((req->dst_rect.w % 32) == 3) || ((req->dst_rect.w % 32) == 1)))) { ret = blit_split_width(mdp, req, src_file, src_start, src_len, dst_file, dst_start, dst_len); goto end; } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) && ((req->dst_rect.h % 32) == 3 || (req->dst_rect.h % 32) == 1))) { ret = blit_split_height(mdp, req, src_file, src_start, src_len, dst_file, dst_start, dst_len); goto end; } ret = mdp_ppp_blit_and_wait(mdp, req, src_file, src_start, src_len, dst_file, dst_start, dst_len); end: return ret; }