/* Copyright (c) 2007, 2012-2013, 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2007 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 #include #include #include "linux/proc_fs.h" #include "mdss_fb.h" #include "mdp3_ppp.h" #include "mdp3_hwio.h" #include "mdss_debug.h" /* SHIM Q Factor */ #define PHI_Q_FACTOR 29 #define PQF_PLUS_5 (PHI_Q_FACTOR + 5) /* due to 32 phases */ #define PQF_PLUS_4 (PHI_Q_FACTOR + 4) #define PQF_PLUS_2 (PHI_Q_FACTOR + 2) /* to get 4.0 */ #define PQF_MINUS_2 (PHI_Q_FACTOR - 2) /* to get 0.25 */ #define PQF_PLUS_5_PLUS_2 (PQF_PLUS_5 + 2) #define PQF_PLUS_5_MINUS_2 (PQF_PLUS_5 - 2) enum { LAYER_FG = 0, LAYER_BG, LAYER_FB, LAYER_MAX, }; static long long mdp_do_div(uint64_t num, uint64_t den) { do_div(num, den); return num; } static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in, uint32_t dim_out, bool is_W, int32_t *phase_init_ptr, uint32_t *phase_step_ptr) { bool rpa_on = false; int init_phase = 0; uint64_t numer = 0; uint64_t denom = 0; int64_t point5 = 1; int64_t one = 1; int64_t k1, k2, k3, k4; /* linear equation coefficients */ uint64_t int_mask; uint64_t fract_mask; uint64_t Os; int64_t Osprime; int64_t Od; int64_t Odprime; int64_t Oreq; int64_t init_phase_temp; int64_t delta; uint32_t mult; /* * The phase accumulator should really be rational for all cases in a * general purpose polyphase scaler for a tiled architecture with * non-zero * origin capability because there is no way to represent * certain scale factors in fixed point regardless of precision. * The error incurred in attempting to use fixed point is most * eggregious for SF where 1/SF is an integral multiple of 1/3. * * Set the RPA flag for this dimension. * * In order for 1/SF (dim_in/dim_out) to be an integral multiple of * 1/3, dim_out must be an integral multiple of 3. */ if (!(dim_out % 3)) { mult = dim_out / 3; rpa_on = (!(dim_in % mult)); } numer = dim_out; denom = dim_in; /* * convert to U30.34 before division * * The K vectors carry 4 extra bits of precision * and are rounded. * * We initially go 5 bits over then round by adding * 1 and right shifting by 1 * so final result is U31.33 */ numer <<= PQF_PLUS_5; /* now calculate the scale factor (aka k3) */ k3 = ((mdp_do_div(numer, denom) + 1) >> 1); /* check scale factor for legal range [0.25 - 4.0] */ if (((k3 >> 4) < (1LL << PQF_MINUS_2)) || ((k3 >> 4) > (1LL << PQF_PLUS_2))) { return -EINVAL; } /* calculate inverse scale factor (aka k1) for phase init */ numer = dim_in; denom = dim_out; numer <<= PQF_PLUS_5; k1 = ((mdp_do_div(numer, denom) + 1) >> 1); /* * calculate initial phase and ROI overfetch */ /* convert point5 & one to S39.24 (will always be positive) */ point5 <<= (PQF_PLUS_4 - 1); one <<= PQF_PLUS_4; k2 = ((k1 - one) >> 1); init_phase = (int)(k2 >> 4); k4 = ((k3 - one) >> 1); if (k3 != one) { /* calculate the masks */ fract_mask = one - 1; int_mask = ~fract_mask; if (!rpa_on) { /* * FIXED POINT IMPLEMENTATION */ if (org) { /* * The complicated case; ROI origin != 0 * init_phase needs to be adjusted * OF is also position dependent */ /* map (org - .5) into destination space */ Os = ((uint64_t) org << 1) - 1; Od = ((k3 * Os) >> 1) + k4; /* take the ceiling */ Odprime = (Od & int_mask); if (Odprime != Od) Odprime += one; /* now map that back to source space */ Osprime = (k1 * (Odprime >> PQF_PLUS_4)) + k2; /* then floor & decrement to calc the required * starting coordinate */ Oreq = (Osprime & int_mask) - one; /* calculate initial phase */ init_phase_temp = Osprime - Oreq; delta = ((int64_t) (org) << PQF_PLUS_4) - Oreq; init_phase_temp -= delta; /* limit to valid range before left shift */ delta = (init_phase_temp & (1LL << 63)) ? 4 : -4; delta <<= PQF_PLUS_4; while (abs((int)(init_phase_temp >> PQF_PLUS_4)) > 4) init_phase_temp += delta; /* * right shift to account for extra bits of * precision */ init_phase = (int)(init_phase_temp >> 4); } } else { /* * RPA IMPLEMENTATION * * init_phase needs to be calculated in all RPA_on * cases because it's a numerator, not a fixed * point value. */ /* map (org - .5) into destination space */ Os = ((uint64_t) org << PQF_PLUS_4) - point5; Od = mdp_do_div((dim_out * (Os + point5)), dim_in); Od -= point5; /* take the ceiling */ Odprime = (Od & int_mask); if (Odprime != Od) Odprime += one; /* now map that back to source space */ Osprime = mdp_do_div((dim_in * (Odprime + point5)), dim_out); Osprime -= point5; /* * then floor & decrement to calculate the required * starting coordinate */ Oreq = (Osprime & int_mask) - one; /* calculate initial phase */ init_phase_temp = Osprime - Oreq; delta = ((int64_t) (org) << PQF_PLUS_4) - Oreq; init_phase_temp -= delta; /* limit to valid range before the left shift */ delta = (init_phase_temp & (1LL << 63)) ? 4 : -4; delta <<= PQF_PLUS_4; while (abs((int)(init_phase_temp >> PQF_PLUS_4)) > 4) init_phase_temp += delta; /* * right shift to account for extra bits of precision */ init_phase = (int)(init_phase_temp >> 4); } } /* return the scale parameters */ *phase_init_ptr = init_phase; *phase_step_ptr = (uint32_t) (k1 >> 4); return 0; } static int scale_idx(int factor) { int idx; if (factor > 80) idx = PPP_DOWNSCALE_PT8TOPT1; else if (factor > 60) idx = PPP_DOWNSCALE_PT6TOPT8; else if (factor > 40) idx = PPP_DOWNSCALE_PT4TOPT6; else idx = PPP_DOWNSCALE_PT2TOPT4; return idx; } inline int32_t comp_conv_rgb2yuv(int32_t comp, int32_t y_high, int32_t y_low, int32_t c_high, int32_t c_low) { if (comp < 0) comp = 0; if (comp > 255) comp = 255; /* clamp */ if (comp < y_low) comp = y_low; if (comp > y_high) comp = y_high; return comp; } static uint32_t conv_rgb2yuv(uint32_t input_pixel, uint16_t *matrix_vector, uint16_t *bv, uint16_t *clamp_vector) { uint8_t input_C2, input_C0, input_C1; uint32_t output; int32_t comp_C2, comp_C1, comp_C0, temp; int32_t temp1, temp2, temp3; int32_t matrix[9]; int32_t bias_vector[3]; int32_t Y_low_limit, Y_high_limit, C_low_limit, C_high_limit; int32_t i; input_C2 = (input_pixel >> 16) & 0xFF; input_C1 = (input_pixel >> 8) & 0xFF; input_C0 = (input_pixel >> 0) & 0xFF; comp_C0 = input_C0; comp_C1 = input_C1; comp_C2 = input_C2; for (i = 0; i < MDP_CSC_SIZE; i++) matrix[i] = ((int32_t) (((int32_t) matrix_vector[i]) << 20)) >> 20; bias_vector[0] = (int32_t) (bv[0] & 0xFF); bias_vector[1] = (int32_t) (bv[1] & 0xFF); bias_vector[2] = (int32_t) (bv[2] & 0xFF); Y_low_limit = (int32_t) clamp_vector[0]; Y_high_limit = (int32_t) clamp_vector[1]; C_low_limit = (int32_t) clamp_vector[2]; C_high_limit = (int32_t) clamp_vector[3]; /* * Color Conversion * reorder input colors */ temp = comp_C2; comp_C2 = comp_C1; comp_C1 = comp_C0; comp_C0 = temp; /* matrix multiplication */ temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2]; temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5]; temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8]; comp_C0 = temp1 + 0x100; comp_C1 = temp2 + 0x100; comp_C2 = temp3 + 0x100; /* take integer part */ comp_C0 >>= 9; comp_C1 >>= 9; comp_C2 >>= 9; /* post bias (+) */ comp_C0 += bias_vector[0]; comp_C1 += bias_vector[1]; comp_C2 += bias_vector[2]; /* limit pixel to 8-bit */ comp_C0 = comp_conv_rgb2yuv(comp_C0, Y_high_limit, Y_low_limit, C_high_limit, C_low_limit); comp_C1 = comp_conv_rgb2yuv(comp_C1, Y_high_limit, Y_low_limit, C_high_limit, C_low_limit); comp_C2 = comp_conv_rgb2yuv(comp_C2, Y_high_limit, Y_low_limit, C_high_limit, C_low_limit); output = (comp_C2 << 16) | (comp_C1 << 8) | comp_C0; return output; } inline void y_h_even_num(struct ppp_img_desc *img) { img->roi.y = (img->roi.y / 2) * 2; img->roi.height = (img->roi.height / 2) * 2; } inline void x_w_even_num(struct ppp_img_desc *img) { img->roi.x = (img->roi.x / 2) * 2; img->roi.width = (img->roi.width / 2) * 2; } bool check_if_rgb(int color) { bool rgb = false; switch (color) { case MDP_RGB_565: case MDP_BGR_565: case MDP_RGB_888: case MDP_BGR_888: case MDP_BGRA_8888: case MDP_RGBA_8888: case MDP_ARGB_8888: case MDP_XRGB_8888: case MDP_RGBX_8888: case MDP_BGRX_8888: rgb = true; default: break; } return rgb; } uint8_t *mdp_adjust_rot_addr(struct ppp_blit_op *iBuf, uint8_t *addr, uint32_t bpp, uint32_t uv, uint32_t layer) { uint32_t ystride = 0; uint32_t h_slice = 1; uint32_t roi_width = 0; uint32_t roi_height = 0; uint32_t color_fmt = 0; if (layer == LAYER_BG) { ystride = iBuf->bg.prop.width * bpp; roi_width = iBuf->bg.roi.width; roi_height = iBuf->bg.roi.height; color_fmt = iBuf->bg.color_fmt; } else { ystride = iBuf->dst.prop.width * bpp; roi_width = iBuf->dst.roi.width; roi_height = iBuf->dst.roi.height; color_fmt = iBuf->dst.color_fmt; } if (uv && ((color_fmt == MDP_Y_CBCR_H2V2) || (color_fmt == MDP_Y_CRCB_H2V2))) h_slice = 2; if (((iBuf->mdp_op & MDPOP_ROT90) == MDPOP_ROT90) ^ ((iBuf->mdp_op & MDPOP_LR) == MDPOP_LR)) { addr += (roi_width - MIN(16, roi_width)) * bpp; } if ((iBuf->mdp_op & MDPOP_UD) == MDPOP_UD) { addr += ((roi_height - MIN(16, roi_height))/h_slice) * ystride; } return addr; } void mdp_adjust_start_addr(struct ppp_blit_op *blit_op, struct ppp_img_desc *img, int v_slice, int h_slice, uint32_t layer) { uint32_t bpp = ppp_bpp(img->color_fmt); int x = img->roi.x; int y = img->roi.y; uint32_t width = img->prop.width; if (img->color_fmt == MDP_Y_CBCR_H2V2_ADRENO && layer == 0) img->p0 += (x + y * ALIGN(width, 32)) * bpp; else if (img->color_fmt == MDP_Y_CBCR_H2V2_VENUS && layer == 0) img->p0 += (x + y * ALIGN(width, 128)) * bpp; else img->p0 += (x + y * width) * bpp; if (layer != LAYER_FG) img->p0 = mdp_adjust_rot_addr(blit_op, img->p0, bpp, 0, layer); if (img->p1) { /* * MDP_Y_CBCR_H2V2/MDP_Y_CRCB_H2V2 cosite for now * we need to shift x direction same as y dir for offsite */ if ((img->color_fmt == MDP_Y_CBCR_H2V2_ADRENO || img->color_fmt == MDP_Y_CBCR_H2V2_VENUS) && layer == 0) img->p1 += ((x / h_slice) * h_slice + ((y == 0) ? 0 : (((y + 1) / v_slice - 1) * (ALIGN(width/2, 32) * 2)))) * bpp; else img->p1 += ((x / h_slice) * h_slice + ((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp; if (layer != LAYER_FG) img->p1 = mdp_adjust_rot_addr(blit_op, img->p1, bpp, 0, layer); } } int load_ppp_lut(int tableType, uint32_t *lut) { int i; uint32_t base_addr; base_addr = tableType ? MDP3_PPP_POST_LUT : MDP3_PPP_PRE_LUT; for (i = 0; i < PPP_LUT_MAX; i++) PPP_WRITEL(lut[i], base_addr + MDP3_PPP_LUTn(i)); return 0; } /* Configure Primary CSC Matrix */ int load_primary_matrix(struct ppp_csc_table *csc) { int i; for (i = 0; i < MDP_CSC_SIZE; i++) PPP_WRITEL(csc->fwd_matrix[i], MDP3_PPP_CSC_PFMVn(i)); for (i = 0; i < MDP_CSC_SIZE; i++) PPP_WRITEL(csc->rev_matrix[i], MDP3_PPP_CSC_PRMVn(i)); for (i = 0; i < MDP_BV_SIZE; i++) PPP_WRITEL(csc->bv[i], MDP3_PPP_CSC_PBVn(i)); for (i = 0; i < MDP_LV_SIZE; i++) PPP_WRITEL(csc->lv[i], MDP3_PPP_CSC_PLVn(i)); return 0; } /* Load Secondary CSC Matrix */ int load_secondary_matrix(struct ppp_csc_table *csc) { int i; for (i = 0; i < MDP_CSC_SIZE; i++) PPP_WRITEL(csc->fwd_matrix[i], MDP3_PPP_CSC_SFMVn(i)); for (i = 0; i < MDP_CSC_SIZE; i++) PPP_WRITEL(csc->rev_matrix[i], MDP3_PPP_CSC_SRMVn(i)); for (i = 0; i < MDP_BV_SIZE; i++) PPP_WRITEL(csc->bv[i], MDP3_PPP_CSC_SBVn(i)); for (i = 0; i < MDP_LV_SIZE; i++) PPP_WRITEL(csc->lv[i], MDP3_PPP_CSC_SLVn(i)); return 0; } int load_csc_matrix(int matrix_type, struct ppp_csc_table *csc) { if (matrix_type == CSC_PRIMARY_MATRIX) return load_primary_matrix(csc); return load_secondary_matrix(csc); } int config_ppp_src(struct ppp_img_desc *src, uint32_t yuv2rgb) { uint32_t val; val = ((src->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) | (src->roi.width & MDP3_PPP_XY_MASK); PPP_WRITEL(val, MDP3_PPP_SRC_SIZE); PPP_WRITEL(src->p0, MDP3_PPP_SRCP0_ADDR); PPP_WRITEL(src->p1, MDP3_PPP_SRCP1_ADDR); PPP_WRITEL(src->p3, MDP3_PPP_SRCP3_ADDR); val = (src->stride0 & MDP3_PPP_STRIDE_MASK) | ((src->stride1 & MDP3_PPP_STRIDE_MASK) << MDP3_PPP_STRIDE1_OFFSET); PPP_WRITEL(val, MDP3_PPP_SRC_YSTRIDE1_ADDR); val = ((src->stride2 & MDP3_PPP_STRIDE_MASK) << MDP3_PPP_STRIDE1_OFFSET); PPP_WRITEL(val, MDP3_PPP_SRC_YSTRIDE2_ADDR); val = ppp_src_config(src->color_fmt); val |= (src->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0; val |= (src->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0; PPP_WRITEL(val, MDP3_PPP_SRC_FORMAT); PPP_WRITEL(ppp_pack_pattern(src->color_fmt, yuv2rgb), MDP3_PPP_SRC_UNPACK_PATTERN1); return 0; } int config_ppp_out(struct ppp_img_desc *dst, uint32_t yuv2rgb) { uint32_t val; bool pseudoplanr_output = false; switch (dst->color_fmt) { case MDP_Y_CBCR_H2V2: case MDP_Y_CRCB_H2V2: case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: pseudoplanr_output = true; break; default: break; } val = ppp_out_config(dst->color_fmt); if (pseudoplanr_output) val |= PPP_DST_PLANE_PSEUDOPLN; PPP_WRITEL(val, MDP3_PPP_OUT_FORMAT); PPP_WRITEL(ppp_pack_pattern(dst->color_fmt, yuv2rgb), MDP3_PPP_OUT_PACK_PATTERN1); val = ((dst->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) | (dst->roi.width & MDP3_PPP_XY_MASK); PPP_WRITEL(val, MDP3_PPP_OUT_SIZE); PPP_WRITEL(dst->p0, MDP3_PPP_OUTP0_ADDR); PPP_WRITEL(dst->p1, MDP3_PPP_OUTP1_ADDR); PPP_WRITEL(dst->p3, MDP3_PPP_OUTP3_ADDR); val = (dst->stride0 & MDP3_PPP_STRIDE_MASK) | ((dst->stride1 & MDP3_PPP_STRIDE_MASK) << MDP3_PPP_STRIDE1_OFFSET); PPP_WRITEL(val, MDP3_PPP_OUT_YSTRIDE1_ADDR); val = ((dst->stride2 & MDP3_PPP_STRIDE_MASK) << MDP3_PPP_STRIDE1_OFFSET); PPP_WRITEL(val, MDP3_PPP_OUT_YSTRIDE2_ADDR); return 0; } int config_ppp_background(struct ppp_img_desc *bg, uint32_t yuv2rgb) { uint32_t val; PPP_WRITEL(bg->p0, MDP3_PPP_BGP0_ADDR); PPP_WRITEL(bg->p1, MDP3_PPP_BGP1_ADDR); PPP_WRITEL(bg->p3, MDP3_PPP_BGP3_ADDR); val = (bg->stride0 & MDP3_PPP_STRIDE_MASK) | ((bg->stride1 & MDP3_PPP_STRIDE_MASK) << MDP3_PPP_STRIDE1_OFFSET); PPP_WRITEL(val, MDP3_PPP_BG_YSTRIDE1_ADDR); val = ((bg->stride2 & MDP3_PPP_STRIDE_MASK) << MDP3_PPP_STRIDE1_OFFSET); PPP_WRITEL(val, MDP3_PPP_BG_YSTRIDE2_ADDR); PPP_WRITEL(ppp_src_config(bg->color_fmt), MDP3_PPP_BG_FORMAT); PPP_WRITEL(ppp_pack_pattern(bg->color_fmt, yuv2rgb), MDP3_PPP_BG_UNPACK_PATTERN1); return 0; } void ppp_edge_rep_luma_pixel(struct ppp_blit_op *blit_op, struct ppp_edge_rep *er) { if (blit_op->mdp_op & MDPOP_ASCALE) { er->is_scale_enabled = 1; if (blit_op->mdp_op & MDPOP_ROT90) { er->dst_roi_width = blit_op->dst.roi.height; er->dst_roi_height = blit_op->dst.roi.width; } else { er->dst_roi_width = blit_op->dst.roi.width; er->dst_roi_height = blit_op->dst.roi.height; } /* * Find out the luma pixels needed for scaling in the * x direction (LEFT and RIGHT). Locations of pixels are * relative to the ROI. Upper-left corner of ROI corresponds * to coordinates (0,0). Also set the number of luma pixel * to repeat. */ if (blit_op->src.roi.width > 3 * er->dst_roi_width) { /* scale factor < 1/3 */ er->luma_interp_point_right = (blit_op->src.roi.width - 1); } else if (blit_op->src.roi.width == 3 * er->dst_roi_width) { /* scale factor == 1/3 */ er->luma_interp_point_right = (blit_op->src.roi.width - 1) + 1; er->luma_repeat_right = 1; } else if ((blit_op->src.roi.width > er->dst_roi_width) && (blit_op->src.roi.width < 3 * er->dst_roi_width)) { /* 1/3 < scale factor < 1 */ er->luma_interp_point_left = -1; er->luma_interp_point_right = (blit_op->src.roi.width - 1) + 1; er->luma_repeat_left = 1; er->luma_repeat_right = 1; } else if (blit_op->src.roi.width == er->dst_roi_width) { /* scale factor == 1 */ er->luma_interp_point_left = -1; er->luma_interp_point_right = (blit_op->src.roi.width - 1) + 2; er->luma_repeat_left = 1; er->luma_repeat_right = 2; } else { /* scale factor > 1 */ er->luma_interp_point_left = -2; er->luma_interp_point_right = (blit_op->src.roi.width - 1) + 2; er->luma_repeat_left = 2; er->luma_repeat_right = 2; } /* * Find out the number of pixels needed for scaling in the * y direction (TOP and BOTTOM). Locations of pixels are * relative to the ROI. Upper-left corner of ROI corresponds * to coordinates (0,0). Also set the number of luma pixel * to repeat. */ if (blit_op->src.roi.height > 3 * er->dst_roi_height) { er->luma_interp_point_bottom = (blit_op->src.roi.height - 1); } else if (blit_op->src.roi.height == 3 * er->dst_roi_height) { er->luma_interp_point_bottom = (blit_op->src.roi.height - 1) + 1; er->luma_repeat_bottom = 1; } else if ((blit_op->src.roi.height > er->dst_roi_height) && (blit_op->src.roi.height < 3 * er->dst_roi_height)) { er->luma_interp_point_top = -1; er->luma_interp_point_bottom = (blit_op->src.roi.height - 1) + 1; er->luma_repeat_top = 1; er->luma_repeat_bottom = 1; } else if (blit_op->src.roi.height == er->dst_roi_height) { er->luma_interp_point_top = -1; er->luma_interp_point_bottom = (blit_op->src.roi.height - 1) + 2; er->luma_repeat_top = 1; er->luma_repeat_bottom = 2; } else { er->luma_interp_point_top = -2; er->luma_interp_point_bottom = (blit_op->src.roi.height - 1) + 2; er->luma_repeat_top = 2; er->luma_repeat_bottom = 2; } } else { /* * Since no scaling needed, Tile Fetch does not require any * more luma pixel than what the ROI contains. */ er->luma_interp_point_right = (int32_t) (blit_op->src.roi.width - 1); er->luma_interp_point_bottom = (int32_t) (blit_op->src.roi.height - 1); } /* After adding the ROI offsets, we have locations of * luma_interp_points relative to the image. */ er->luma_interp_point_left += (int32_t) (blit_op->src.roi.x); er->luma_interp_point_right += (int32_t) (blit_op->src.roi.x); er->luma_interp_point_top += (int32_t) (blit_op->src.roi.y); er->luma_interp_point_bottom += (int32_t) (blit_op->src.roi.y); } void ppp_edge_rep_chroma_pixel(struct ppp_blit_op *blit_op, struct ppp_edge_rep *er) { bool chroma_edge_enable = true; uint32_t is_yuv_offsite_vertical = 0; /* find out which chroma pixels are needed for chroma upsampling. */ switch (blit_op->src.color_fmt) { case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: case MDP_YCRYCB_H2V1: er->chroma_interp_point_left = er->luma_interp_point_left >> 1; er->chroma_interp_point_right = (er->luma_interp_point_right + 1) >> 1; er->chroma_interp_point_top = er->luma_interp_point_top; er->chroma_interp_point_bottom = er->luma_interp_point_bottom; break; case MDP_Y_CBCR_H2V2: case MDP_Y_CBCR_H2V2_ADRENO: case MDP_Y_CBCR_H2V2_VENUS: case MDP_Y_CRCB_H2V2: er->chroma_interp_point_left = er->luma_interp_point_left >> 1; er->chroma_interp_point_right = (er->luma_interp_point_right + 1) >> 1; er->chroma_interp_point_top = (er->luma_interp_point_top - 1) >> 1; er->chroma_interp_point_bottom = (er->luma_interp_point_bottom + 1) >> 1; is_yuv_offsite_vertical = 1; break; default: chroma_edge_enable = false; er->chroma_interp_point_left = er->luma_interp_point_left; er->chroma_interp_point_right = er->luma_interp_point_right; er->chroma_interp_point_top = er->luma_interp_point_top; er->chroma_interp_point_bottom = er->luma_interp_point_bottom; break; } if (chroma_edge_enable) { /* Defines which chroma pixels belongs to the roi */ switch (blit_op->src.color_fmt) { case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: case MDP_YCRYCB_H2V1: er->chroma_bound_left = blit_op->src.roi.x / 2; /* there are half as many chroma pixel as luma pixels */ er->chroma_bound_right = (blit_op->src.roi.width + blit_op->src.roi.x - 1) / 2; er->chroma_bound_top = blit_op->src.roi.y; er->chroma_bound_bottom = (blit_op->src.roi.height + blit_op->src.roi.y - 1); break; case MDP_Y_CBCR_H2V2: case MDP_Y_CBCR_H2V2_ADRENO: case MDP_Y_CBCR_H2V2_VENUS: case MDP_Y_CRCB_H2V2: /* * cosite in horizontal dir, and offsite in vertical dir * width of chroma ROI is 1/2 of size of luma ROI * height of chroma ROI is 1/2 of size of luma ROI */ er->chroma_bound_left = blit_op->src.roi.x / 2; er->chroma_bound_right = (blit_op->src.roi.width + blit_op->src.roi.x - 1) / 2; er->chroma_bound_top = blit_op->src.roi.y / 2; er->chroma_bound_bottom = (blit_op->src.roi.height + blit_op->src.roi.y - 1) / 2; break; default: /* * If no valid chroma sub-sampling format specified, * assume 4:4:4 ( i.e. fully sampled). */ er->chroma_bound_left = blit_op->src.roi.x; er->chroma_bound_right = blit_op->src.roi.width + blit_op->src.roi.x - 1; er->chroma_bound_top = blit_op->src.roi.y; er->chroma_bound_bottom = (blit_op->src.roi.height + blit_op->src.roi.y - 1); break; } /* * Knowing which chroma pixels are needed, and which chroma * pixels belong to the ROI (i.e. available for fetching ), * calculate how many chroma pixels Tile Fetch needs to * duplicate. If any required chroma pixels falls outside * of the ROI, Tile Fetch must obtain them by replicating * pixels. */ if (er->chroma_bound_left > er->chroma_interp_point_left) er->chroma_repeat_left = er->chroma_bound_left - er->chroma_interp_point_left; else er->chroma_repeat_left = 0; if (er->chroma_interp_point_right > er->chroma_bound_right) er->chroma_repeat_right = er->chroma_interp_point_right - er->chroma_bound_right; else er->chroma_repeat_right = 0; if (er->chroma_bound_top > er->chroma_interp_point_top) er->chroma_repeat_top = er->chroma_bound_top - er->chroma_interp_point_top; else er->chroma_repeat_top = 0; if (er->chroma_interp_point_bottom > er->chroma_bound_bottom) er->chroma_repeat_bottom = er->chroma_interp_point_bottom - er->chroma_bound_bottom; else er->chroma_repeat_bottom = 0; if (er->is_scale_enabled && (blit_op->src.roi.height == 1) && is_yuv_offsite_vertical) { er->chroma_repeat_bottom = 3; er->chroma_repeat_top = 0; } } } int config_ppp_edge_rep(struct ppp_blit_op *blit_op) { uint32_t reg = 0; struct ppp_edge_rep er; memset(&er, 0, sizeof(er)); ppp_edge_rep_luma_pixel(blit_op, &er); /* * After adding the ROI offsets, we have locations of * chroma_interp_points relative to the image. */ er.chroma_interp_point_left = er.luma_interp_point_left; er.chroma_interp_point_right = er.luma_interp_point_right; er.chroma_interp_point_top = er.luma_interp_point_top; er.chroma_interp_point_bottom = er.luma_interp_point_bottom; ppp_edge_rep_chroma_pixel(blit_op, &er); /* ensure repeats are >=0 and no larger than 3 pixels */ if ((er.chroma_repeat_left < 0) || (er.chroma_repeat_right < 0) || (er.chroma_repeat_top < 0) || (er.chroma_repeat_bottom < 0)) return -EINVAL; if ((er.chroma_repeat_left > 3) || (er.chroma_repeat_right > 3) || (er.chroma_repeat_top > 3) || (er.chroma_repeat_bottom > 3)) return -EINVAL; if ((er.luma_repeat_left < 0) || (er.luma_repeat_right < 0) || (er.luma_repeat_top < 0) || (er.luma_repeat_bottom < 0)) return -EINVAL; if ((er.luma_repeat_left > 3) || (er.luma_repeat_right > 3) || (er.luma_repeat_top > 3) || (er.luma_repeat_bottom > 3)) return -EINVAL; reg |= (er.chroma_repeat_left & 3) << MDP_LEFT_CHROMA; reg |= (er.chroma_repeat_right & 3) << MDP_RIGHT_CHROMA; reg |= (er.chroma_repeat_top & 3) << MDP_TOP_CHROMA; reg |= (er.chroma_repeat_bottom & 3) << MDP_BOTTOM_CHROMA; reg |= (er.luma_repeat_left & 3) << MDP_LEFT_LUMA; reg |= (er.luma_repeat_right & 3) << MDP_RIGHT_LUMA; reg |= (er.luma_repeat_top & 3) << MDP_TOP_LUMA; reg |= (er.luma_repeat_bottom & 3) << MDP_BOTTOM_LUMA; PPP_WRITEL(reg, MDP3_PPP_SRC_EDGE_REP); return 0; } int config_ppp_bg_edge_rep(struct ppp_blit_op *blit_op) { uint32_t reg = 0; switch (blit_op->dst.color_fmt) { case MDP_Y_CBCR_H2V2: case MDP_Y_CRCB_H2V2: if (blit_op->dst.roi.y == 0) reg |= BIT(MDP_TOP_CHROMA); if ((blit_op->dst.roi.y + blit_op->dst.roi.height) == blit_op->dst.prop.height) { reg |= BIT(MDP_BOTTOM_CHROMA); } if (((blit_op->dst.roi.x + blit_op->dst.roi.width) == blit_op->dst.prop.width) && ((blit_op->dst.roi.width % 2) == 0)) reg |= BIT(MDP_RIGHT_CHROMA); break; case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: case MDP_YCRYCB_H2V1: if (((blit_op->dst.roi.x + blit_op->dst.roi.width) == blit_op->dst.prop.width) && ((blit_op->dst.roi.width % 2) == 0)) reg |= BIT(MDP_RIGHT_CHROMA); break; default: break; } PPP_WRITEL(reg, MDP3_PPP_BG_EDGE_REP); return 0; } int config_ppp_lut(uint32_t *pppop_reg_ptr, int lut_c0_en, int lut_c1_en, int lut_c2_en) { if (lut_c0_en) *pppop_reg_ptr |= MDP_LUT_C0_EN; if (lut_c1_en) *pppop_reg_ptr |= MDP_LUT_C1_EN; if (lut_c2_en) *pppop_reg_ptr |= MDP_LUT_C2_EN; return 0; } int config_ppp_scale(struct ppp_blit_op *blit_op, uint32_t *pppop_reg_ptr) { struct ppp_img_desc *src = &blit_op->src; struct ppp_img_desc *dst = &blit_op->dst; uint32_t dstW, dstH; uint32_t x_fac, y_fac; uint32_t mdp_blur = 0; uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; int x_idx, y_idx; if (blit_op->mdp_op & MDPOP_ASCALE) { if (blit_op->mdp_op & MDPOP_ROT90) { dstW = dst->roi.height; dstH = dst->roi.width; } else { dstW = dst->roi.width; dstH = dst->roi.height; } *pppop_reg_ptr |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); mdp_blur = blit_op->mdp_op & MDPOP_BLUR; if ((dstW != src->roi.width) || (dstH != src->roi.height) || mdp_blur) { /* * Use source origin as 0 for computing initial * phase and step size. Incorrect initial phase and * step size value results in green line issue. */ mdp_calc_scale_params(0, blit_op->src.roi.width, dstW, 1, &phase_init_x, &phase_step_x); mdp_calc_scale_params(0, blit_op->src.roi.height, dstH, 0, &phase_init_y, &phase_step_y); PPP_WRITEL(phase_init_x, MDP3_PPP_SCALE_PHASEX_INIT); PPP_WRITEL(phase_init_y, MDP3_PPP_SCALE_PHASEY_INIT); PPP_WRITEL(phase_step_x, MDP3_PPP_SCALE_PHASEX_STEP); PPP_WRITEL(phase_step_y, MDP3_PPP_SCALE_PHASEY_STEP); if (dstW > src->roi.width || dstH > src->roi.height) ppp_load_up_lut(); if (mdp_blur) ppp_load_gaussian_lut(); if (dstW <= src->roi.width) { x_fac = (dstW * 100) / src->roi.width; x_idx = scale_idx(x_fac); ppp_load_x_scale_table(x_idx); } if (dstH <= src->roi.height) { y_fac = (dstH * 100) / src->roi.height; y_idx = scale_idx(y_fac); ppp_load_y_scale_table(y_idx); } } else { blit_op->mdp_op &= ~(MDPOP_ASCALE); } } config_ppp_edge_rep(blit_op); config_ppp_bg_edge_rep(blit_op); return 0; } int config_ppp_csc(int src_color, int dst_color, uint32_t *pppop_reg_ptr) { bool inputRGB, outputRGB; inputRGB = check_if_rgb(src_color); outputRGB = check_if_rgb(dst_color); if ((!inputRGB) && (outputRGB)) *pppop_reg_ptr |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON; if ((inputRGB) && (!outputRGB)) *pppop_reg_ptr |= PPP_OP_CONVERT_ON; return 0; } int config_ppp_blend(struct ppp_blit_op *blit_op, uint32_t *pppop_reg_ptr, bool is_yuv_smart_blit, int smart_blit_bg_alpha) { struct ppp_csc_table *csc; uint32_t alpha, trans_color; uint32_t val = 0; int c_fmt = blit_op->src.color_fmt; int bg_alpha; csc = ppp_csc_rgb2yuv(); alpha = blit_op->blend.const_alpha; trans_color = blit_op->blend.trans_color; if (blit_op->mdp_op & MDPOP_FG_PM_ALPHA) { if (ppp_per_p_alpha(c_fmt)) { *pppop_reg_ptr |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | PPP_OP_BLEND_CONSTANT_ALPHA; } else { if ((blit_op->mdp_op & MDPOP_ALPHAB) && (blit_op->blend.const_alpha == 0xff)) { blit_op->mdp_op &= ~(MDPOP_ALPHAB); } if ((blit_op->mdp_op & MDPOP_ALPHAB) || (blit_op->mdp_op & MDPOP_TRANSP)) { *pppop_reg_ptr |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | PPP_OP_BLEND_CONSTANT_ALPHA | PPP_OP_BLEND_ALPHA_BLEND_NORMAL; } } bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | PPP_BLEND_BG_ALPHA_REVERSE; if ((ppp_per_p_alpha(c_fmt)) && !(blit_op->mdp_op & MDPOP_LAYER_IS_FG)) { bg_alpha |= PPP_BLEND_BG_SRCPIXEL_ALPHA; } else { bg_alpha |= PPP_BLEND_BG_CONSTANT_ALPHA; bg_alpha |= blit_op->blend.const_alpha << 24; } PPP_WRITEL(bg_alpha, MDP3_PPP_BLEND_BG_ALPHA_SEL); if (blit_op->mdp_op & MDPOP_TRANSP) *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP; } else if (ppp_per_p_alpha(c_fmt)) { if (blit_op->mdp_op & MDPOP_LAYER_IS_FG) *pppop_reg_ptr |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | PPP_OP_BLEND_CONSTANT_ALPHA; else *pppop_reg_ptr |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | PPP_OP_BLEND_SRCPIXEL_ALPHA; PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL); } else { if ((blit_op->mdp_op & MDPOP_ALPHAB) && (blit_op->blend.const_alpha == 0xff)) { blit_op->mdp_op &= ~(MDPOP_ALPHAB); } if ((blit_op->mdp_op & MDPOP_ALPHAB) || (blit_op->mdp_op & MDPOP_TRANSP)) { *pppop_reg_ptr |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | PPP_OP_BLEND_CONSTANT_ALPHA | PPP_OP_BLEND_ALPHA_BLEND_NORMAL; } if (blit_op->mdp_op & MDPOP_TRANSP) *pppop_reg_ptr |= PPP_BLEND_CALPHA_TRNASP; if (is_yuv_smart_blit) { *pppop_reg_ptr |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | PPP_OP_BLEND_BG_ALPHA | PPP_OP_BLEND_EQ_REVERSE; if (smart_blit_bg_alpha < 0xFF) bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | PPP_BLEND_BG_DSTPIXEL_ALPHA; else bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL | PPP_BLEND_BG_DSTPIXEL_ALPHA | PPP_BLEND_BG_CONSTANT_ALPHA; bg_alpha |= smart_blit_bg_alpha << 24; PPP_WRITEL(bg_alpha, MDP3_PPP_BLEND_BG_ALPHA_SEL); } else { PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL); } } if (*pppop_reg_ptr & PPP_OP_BLEND_ON) { if (is_yuv_smart_blit) config_ppp_background(&blit_op->bg, 1); else config_ppp_background(&blit_op->bg, 0); if (blit_op->dst.color_fmt == MDP_YCRYCB_H2V1) { *pppop_reg_ptr |= PPP_OP_BG_CHROMA_H2V1; if (blit_op->mdp_op & MDPOP_TRANSP) { trans_color = conv_rgb2yuv(trans_color, &csc->fwd_matrix[0], &csc->bv[0], &csc->lv[0]); } } } if (is_yuv_smart_blit) { PPP_WRITEL(0, MDP3_PPP_BLEND_PARAM); } else { val = (alpha << MDP_BLEND_CONST_ALPHA); val |= (trans_color & MDP_BLEND_TRASP_COL_MASK); PP_WRITEL(val, MDP3_PPP_BLEND_PARAM); } return 0; } int config_ppp_rotation(uint32_t mdp_op, uint32_t *pppop_reg_ptr) { *pppop_reg_ptr |= PPP_OP_ROT_ON; if (mdp_op & MDPOP_ROT90) *pppop_reg_ptr |= PPP_OP_ROT_90; if (mdp_op & MDPOP_LR) *pppop_reg_ptr |= PPP_OP_FLIP_LR; if (mdp_op & MDPOP_UD) *pppop_reg_ptr |= PPP_OP_FLIP_UD; return 0; } int config_ppp_op_mode(struct ppp_blit_op *blit_op) { uint32_t yuv2rgb; uint32_t ppp_operation_reg = 0; int sv_slice, sh_slice; int dv_slice, dh_slice; static struct ppp_img_desc bg_img_param; static int bg_alpha; static int bg_mdp_ops; bool is_yuv_smart_blit = false; /* * Detect YUV smart blit, * Check cached BG image plane 0 address is not NILL and * source color format is YUV than it is YUV smart blit * mark is_yuv_smart_blit true. */ if ((bg_img_param.p0) && (!(check_if_rgb(blit_op->src.color_fmt)))) is_yuv_smart_blit = true; sv_slice = sh_slice = dv_slice = dh_slice = 1; ppp_operation_reg |= ppp_dst_op_reg(blit_op->dst.color_fmt); switch (blit_op->dst.color_fmt) { case MDP_Y_CBCR_H2V2: case MDP_Y_CRCB_H2V2: y_h_even_num(&blit_op->dst); y_h_even_num(&blit_op->src); dv_slice = 2; /* fall-through */ case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: case MDP_YCRYCB_H2V1: x_w_even_num(&blit_op->dst); x_w_even_num(&blit_op->src); dh_slice = 2; break; default: break; } ppp_operation_reg |= ppp_src_op_reg(blit_op->src.color_fmt); switch (blit_op->src.color_fmt) { case MDP_Y_CBCR_H2V2: case MDP_Y_CBCR_H2V2_ADRENO: case MDP_Y_CBCR_H2V2_VENUS: case MDP_Y_CRCB_H2V2: sh_slice = sv_slice = 2; break; case MDP_YCRYCB_H2V1: x_w_even_num(&blit_op->dst); x_w_even_num(&blit_op->src); /* fall-through */ case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: sh_slice = 2; break; default: break; } config_ppp_csc(blit_op->src.color_fmt, blit_op->dst.color_fmt, &ppp_operation_reg); yuv2rgb = ppp_operation_reg & PPP_OP_CONVERT_YCBCR2RGB; if (blit_op->mdp_op & MDPOP_DITHER) ppp_operation_reg |= PPP_OP_DITHER_EN; if (blit_op->mdp_op & MDPOP_ROTATION) config_ppp_rotation(blit_op->mdp_op, &ppp_operation_reg); if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO) { blit_op->src.stride0 = ALIGN(blit_op->src.prop.width, 32) * ppp_bpp(blit_op->src.color_fmt); blit_op->src.stride1 = 2 * ALIGN(blit_op->src.prop.width/2, 32); } else if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_VENUS) { blit_op->src.stride0 = ALIGN(blit_op->src.prop.width, 128) * ppp_bpp(blit_op->src.color_fmt); blit_op->src.stride1 = blit_op->src.stride0; } else { blit_op->src.stride0 = blit_op->src.prop.width * ppp_bpp(blit_op->src.color_fmt); blit_op->src.stride1 = blit_op->src.stride0; } blit_op->dst.stride0 = blit_op->dst.prop.width * ppp_bpp(blit_op->dst.color_fmt); if (ppp_multi_plane(blit_op->dst.color_fmt)) { blit_op->dst.p1 = blit_op->dst.p0; blit_op->dst.p1 += blit_op->dst.prop.width * blit_op->dst.prop.height * ppp_bpp(blit_op->dst.color_fmt); } else { blit_op->dst.p1 = NULL; } if ((bg_img_param.p0) && (!(blit_op->mdp_op & MDPOP_SMART_BLIT))) { /* * Use cached smart blit BG layer info in * smart Blit FG request */ blit_op->bg = bg_img_param; if (check_if_rgb(blit_op->bg.color_fmt)) { blit_op->bg.p1 = 0; blit_op->bg.stride1 = 0; } memset(&bg_img_param, 0, sizeof(bg_img_param)); } else { blit_op->bg = blit_op->dst; } /* Cache smart blit BG layer info */ if (blit_op->mdp_op & MDPOP_SMART_BLIT) bg_img_param = blit_op->src; /* Jumping from Y-Plane to Chroma Plane */ /* first pixel addr calculation */ mdp_adjust_start_addr(blit_op, &blit_op->src, sv_slice, sh_slice, LAYER_FG); mdp_adjust_start_addr(blit_op, &blit_op->bg, dv_slice, dh_slice, LAYER_BG); mdp_adjust_start_addr(blit_op, &blit_op->dst, dv_slice, dh_slice, LAYER_FB); config_ppp_scale(blit_op, &ppp_operation_reg); config_ppp_blend(blit_op, &ppp_operation_reg, is_yuv_smart_blit, bg_alpha); config_ppp_src(&blit_op->src, yuv2rgb); config_ppp_out(&blit_op->dst, yuv2rgb); /* Cache Smart blit BG alpha adn MDP OP values */ if (blit_op->mdp_op & MDPOP_SMART_BLIT) { bg_alpha = blit_op->blend.const_alpha; bg_mdp_ops = blit_op->mdp_op; } else { bg_alpha = 0; bg_mdp_ops = 0; } pr_debug("BLIT FG Param Fmt %d (x %d,y %d,w %d,h %d), ", blit_op->src.color_fmt, blit_op->src.prop.x, blit_op->src.prop.y, blit_op->src.prop.width, blit_op->src.prop.height); pr_debug("ROI(x %d,y %d,w %d, h %d) ", blit_op->src.roi.x, blit_op->src.roi.y, blit_op->src.roi.width, blit_op->src.roi.height); pr_debug("Addr_P0 %pK, Stride S0 %d Addr_P1 %pK, Stride S1 %d\n", blit_op->src.p0, blit_op->src.stride0, blit_op->src.p1, blit_op->src.stride1); if (blit_op->bg.p0 != blit_op->dst.p0) { pr_debug("BLIT BG Param Fmt %d (x %d,y %d,w %d,h %d), ", blit_op->bg.color_fmt, blit_op->bg.prop.x, blit_op->bg.prop.y, blit_op->bg.prop.width, blit_op->bg.prop.height); pr_debug("ROI(x %d,y %d, w %d, h %d) ", blit_op->bg.roi.x, blit_op->bg.roi.y, blit_op->bg.roi.width, blit_op->bg.roi.height); pr_debug("Addr %pK, Stride S0 %d Addr_P1 %pK, Stride S1 %d\n", blit_op->bg.p0, blit_op->bg.stride0, blit_op->bg.p1, blit_op->bg.stride1); } pr_debug("BLIT FB Param Fmt %d (x %d,y %d,w %d,h %d), ", blit_op->dst.color_fmt, blit_op->dst.prop.x, blit_op->dst.prop.y, blit_op->dst.prop.width, blit_op->dst.prop.height); pr_debug("ROI(x %d,y %d, w %d, h %d) ", blit_op->dst.roi.x, blit_op->dst.roi.y, blit_op->dst.roi.width, blit_op->dst.roi.height); pr_debug("Addr %p, Stride S0 %d Addr_P1 %p, Stride S1 %d\n", blit_op->dst.p0, blit_op->dst.stride0, blit_op->dst.p1, blit_op->dst.stride1); PPP_WRITEL(ppp_operation_reg, MDP3_PPP_OP_MODE); mb(); /* make sure everything is written before enable */ MDSS_XLOG(ppp_operation_reg, blit_op->src.roi.x, blit_op->src.roi.y, blit_op->src.roi.width, blit_op->src.roi.height); MDSS_XLOG(blit_op->dst.roi.x, blit_op->dst.roi.y, blit_op->dst.roi.width, blit_op->dst.roi.height); return 0; } void ppp_enable(void) { PPP_WRITEL(0x1000, 0x30); mb(); /* make sure everything is written before enable */ } int mdp3_ppp_init(void) { load_ppp_lut(LUT_PRE_TABLE, ppp_default_pre_lut()); load_ppp_lut(LUT_POST_TABLE, ppp_default_post_lut()); load_csc_matrix(CSC_PRIMARY_MATRIX, ppp_csc_rgb2yuv()); load_csc_matrix(CSC_SECONDARY_MATRIX, ppp_csc_table2()); return 0; }