/* Copyright (c) 2007, 2013-2014 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 #include #include #include #include "linux/proc_fs.h" #include #include "mdss_fb.h" #include "mdp3_ppp.h" #include "mdp3_hwio.h" #include "mdp3.h" #define MDP_IS_IMGTYPE_BAD(x) ((x) >= MDP_IMGTYPE_LIMIT) #define MDP_RELEASE_BW_TIMEOUT 50 #define MDP_PPP_MAX_BPP 4 #define MDP_PPP_DYNAMIC_FACTOR 3 #define MDP_PPP_MAX_READ_WRITE 3 #define ENABLE_SOLID_FILL 0x2 #define DISABLE_SOLID_FILL 0x0 struct ppp_resource ppp_res; static const bool valid_fmt[MDP_IMGTYPE_LIMIT] = { [MDP_RGB_565] = true, [MDP_BGR_565] = true, [MDP_RGB_888] = true, [MDP_BGR_888] = true, [MDP_BGRA_8888] = true, [MDP_RGBA_8888] = true, [MDP_ARGB_8888] = true, [MDP_XRGB_8888] = true, [MDP_RGBX_8888] = true, [MDP_Y_CRCB_H2V2] = true, [MDP_Y_CBCR_H2V2] = true, [MDP_Y_CBCR_H2V2_ADRENO] = true, [MDP_Y_CBCR_H2V2_VENUS] = true, [MDP_YCRYCB_H2V1] = true, [MDP_Y_CBCR_H2V1] = true, [MDP_Y_CRCB_H2V1] = true, [MDP_BGRX_8888] = true, }; #define MAX_LIST_WINDOW 16 #define MDP3_PPP_MAX_LIST_REQ 8 struct blit_req_list { int count; struct mdp_blit_req req_list[MAX_LIST_WINDOW]; struct mdp3_img_data src_data[MAX_LIST_WINDOW]; struct mdp3_img_data dst_data[MAX_LIST_WINDOW]; struct sync_fence *acq_fen[MDP_MAX_FENCE_FD]; u32 acq_fen_cnt; int cur_rel_fen_fd; struct sync_pt *cur_rel_sync_pt; struct sync_fence *cur_rel_fence; struct sync_fence *last_rel_fence; }; struct blit_req_queue { struct blit_req_list req[MDP3_PPP_MAX_LIST_REQ]; int count; int push_idx; int pop_idx; }; struct ppp_status { bool wait_for_pop; struct completion ppp_comp; struct completion pop_q_comp; struct mutex req_mutex; /* Protect request queue */ struct mutex config_ppp_mutex; /* Only one client configure register */ struct msm_fb_data_type *mfd; struct work_struct blit_work; struct blit_req_queue req_q; struct sw_sync_timeline *timeline; int timeline_value; struct timer_list free_bw_timer; struct work_struct free_bw_work; bool bw_update; bool bw_on; u32 mdp_clk; }; static struct ppp_status *ppp_stat; int ppp_get_bpp(uint32_t format, uint32_t fb_format) { int bpp = -EINVAL; if (format == MDP_FB_FORMAT) format = fb_format; bpp = ppp_bpp(format); if (bpp <= 0) pr_err("%s incorrect format %d\n", __func__, format); return bpp; } int mdp3_ppp_get_img(struct mdp_img *img, struct mdp_blit_req *req, struct mdp3_img_data *data) { struct msmfb_data fb_data; fb_data.flags = img->priv; fb_data.memory_id = img->memory_id; fb_data.offset = 0; return mdp3_get_img(&fb_data, data); } /* Check format */ int mdp3_ppp_verify_fmt(struct mdp_blit_req *req) { if (MDP_IS_IMGTYPE_BAD(req->src.format) || MDP_IS_IMGTYPE_BAD(req->dst.format)) { pr_err("%s: Color format out of range\n", __func__); return -EINVAL; } if (!valid_fmt[req->src.format] || !valid_fmt[req->dst.format]) { pr_err("%s: Color format not supported\n", __func__); return -EINVAL; } return 0; } /* Check resolution */ int mdp3_ppp_verify_res(struct mdp_blit_req *req) { if ((req->src.width == 0) || (req->src.height == 0) || (req->src_rect.w == 0) || (req->src_rect.h == 0) || (req->dst.width == 0) || (req->dst.height == 0) || (req->dst_rect.w == 0) || (req->dst_rect.h == 0)) { pr_err("%s: Height/width can't be 0\n", __func__); return -EINVAL; } if (((req->src_rect.x + req->src_rect.w) > req->src.width) || ((req->src_rect.y + req->src_rect.h) > req->src.height)) { pr_err("%s: src roi larger than boundary\n", __func__); return -EINVAL; } if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) || ((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) { pr_err("%s: dst roi larger than boundary\n", __func__); return -EINVAL; } return 0; } /* scaling range check */ int mdp3_ppp_verify_scale(struct mdp_blit_req *req) { u32 src_width, src_height, dst_width, dst_height; src_width = req->src_rect.w; src_height = req->src_rect.h; if (req->flags & MDP_ROT_90) { dst_width = req->dst_rect.h; dst_height = req->dst_rect.w; } else { dst_width = req->dst_rect.w; dst_height = req->dst_rect.h; } switch (req->dst.format) { case MDP_Y_CRCB_H2V2: case MDP_Y_CBCR_H2V2: src_width = (src_width / 2) * 2; src_height = (src_height / 2) * 2; dst_width = (dst_width / 2) * 2; dst_height = (dst_height / 2) * 2; break; case MDP_Y_CRCB_H2V1: case MDP_Y_CBCR_H2V1: case MDP_YCRYCB_H2V1: src_width = (src_width / 2) * 2; dst_width = (dst_width / 2) * 2; break; default: break; } if (((MDP_SCALE_Q_FACTOR * dst_width) / src_width > MDP_MAX_X_SCALE_FACTOR) || ((MDP_SCALE_Q_FACTOR * dst_width) / src_width < MDP_MIN_X_SCALE_FACTOR)) { pr_err("%s: x req scale factor beyond capability\n", __func__); return -EINVAL; } if (((MDP_SCALE_Q_FACTOR * dst_height) / src_height > MDP_MAX_Y_SCALE_FACTOR) || ((MDP_SCALE_Q_FACTOR * dst_height) / src_height < MDP_MIN_Y_SCALE_FACTOR)) { pr_err("%s: y req scale factor beyond capability\n", __func__); return -EINVAL; } return 0; } /* operation check */ int mdp3_ppp_verify_op(struct mdp_blit_req *req) { if (req->flags & MDP_DEINTERLACE) { pr_err("\n%s(): deinterlace not supported", __func__); return -EINVAL; } if (req->flags & MDP_SHARPENING) { pr_err("\n%s(): sharpening not supported", __func__); return -EINVAL; } return 0; } int mdp3_ppp_verify_req(struct mdp_blit_req *req) { int rc; if (req == NULL) { pr_err("%s: req == null\n", __func__); return -EINVAL; } rc = mdp3_ppp_verify_fmt(req); rc |= mdp3_ppp_verify_res(req); rc |= mdp3_ppp_verify_scale(req); rc |= mdp3_ppp_verify_op(req); return rc; } int mdp3_ppp_pipe_wait(void) { int ret = 1; /* * wait 200 ms for ppp operation to complete before declaring * the MDP hung */ ret = wait_for_completion_timeout( &ppp_stat->ppp_comp, msecs_to_jiffies(200)); if (!ret) pr_err("%s: Timed out waiting for the MDP.\n", __func__); return ret; } uint32_t mdp3_calc_tpval(struct ppp_img_desc *img, uint32_t old_tp) { uint32_t tpVal; uint8_t plane_tp; tpVal = 0; if ((img->color_fmt == MDP_RGB_565) || (img->color_fmt == MDP_BGR_565)) { /* transparent color conversion into 24 bpp */ plane_tp = (uint8_t) ((old_tp & 0xF800) >> 11); tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 16; plane_tp = (uint8_t) (old_tp & 0x1F); tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 8; plane_tp = (uint8_t) ((old_tp & 0x7E0) >> 5); tpVal |= ((plane_tp << 2) | ((plane_tp & 0x30) >> 4)); } else { /* 24bit RGB to RBG conversion */ tpVal = (old_tp & 0xFF00) >> 8; tpVal |= (old_tp & 0xFF) << 8; tpVal |= (old_tp & 0xFF0000); } return tpVal; } static void mdp3_ppp_intr_handler(int type, void *arg) { complete(&ppp_stat->ppp_comp); } static int mdp3_ppp_callback_setup(void) { int rc; struct mdp3_intr_cb ppp_done_cb = { .cb = mdp3_ppp_intr_handler, .data = NULL, }; rc = mdp3_set_intr_callback(MDP3_PPP_DONE, &ppp_done_cb); return rc; } void mdp3_ppp_kickoff(void) { init_completion(&ppp_stat->ppp_comp); mdp3_irq_enable(MDP3_PPP_DONE); ppp_enable(); mdp3_ppp_pipe_wait(); mdp3_irq_disable(MDP3_PPP_DONE); } u32 mdp3_clk_calc(struct msm_fb_data_type *mfd, struct blit_req_list *lreq) { struct mdss_panel_info *panel_info = mfd->panel_info; int i, lcount = 0; struct mdp_blit_req *req; u32 total_pixel; u32 mdp_clk_rate = MDP_CORE_CLK_RATE_SVS; total_pixel = panel_info->xres * panel_info->yres; if (total_pixel > SVS_MAX_PIXEL) return MDP_CORE_CLK_RATE_MAX; for (i = 0; i < lcount; i++) { req = &(lreq->req_list[i]); if (req->src_rect.h != req->dst_rect.h || req->src_rect.w != req->dst_rect.w) { mdp_clk_rate = MDP_CORE_CLK_RATE_MAX; break; } } return mdp_clk_rate; } struct bpp_info { int bpp_num; int bpp_den; int bpp_pln; }; int mdp3_get_bpp_info(int format, struct bpp_info *bpp) { int rc = 0; switch (format) { case MDP_RGB_565: case MDP_BGR_565: bpp->bpp_num = 2; bpp->bpp_den = 1; bpp->bpp_pln = 2; break; case MDP_RGB_888: case MDP_BGR_888: bpp->bpp_num = 3; bpp->bpp_den = 1; bpp->bpp_pln = 3; break; case MDP_BGRA_8888: case MDP_RGBA_8888: case MDP_ARGB_8888: case MDP_XRGB_8888: case MDP_RGBX_8888: case MDP_BGRX_8888: bpp->bpp_num = 4; bpp->bpp_den = 1; bpp->bpp_pln = 4; break; case MDP_Y_CRCB_H2V2: case MDP_Y_CBCR_H2V2: case MDP_Y_CBCR_H2V2_ADRENO: case MDP_Y_CBCR_H2V2_VENUS: bpp->bpp_num = 3; bpp->bpp_den = 2; bpp->bpp_pln = 1; break; case MDP_Y_CBCR_H2V1: case MDP_Y_CRCB_H2V1: bpp->bpp_num = 2; bpp->bpp_den = 1; bpp->bpp_pln = 1; break; case MDP_YCRYCB_H2V1: bpp->bpp_num = 2; bpp->bpp_den = 1; bpp->bpp_pln = 2; break; default: rc = -EINVAL; } return rc; } u64 mdp3_adjust_scale_factor(struct mdp_blit_req *req, u32 bw_req, int bpp) { int src_h, src_w; int dst_h, dst_w; src_h = req->src_rect.h; src_w = req->src_rect.w; dst_h = req->dst_rect.h; dst_w = req->dst_rect.w; if ((!(req->flags & MDP_ROT_90) && src_h == dst_h && src_w == dst_w) || ((req->flags & MDP_ROT_90) && src_h == dst_w && src_w == dst_h)) return bw_req; bw_req = (bw_req + (bw_req * dst_h) / (4 * src_h)); bw_req = (bw_req + (bw_req * dst_w) / (4 * src_w) + (bw_req * dst_w) / (bpp * src_w)); return bw_req; } int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, struct blit_req_list *lreq) { struct mdss_panel_info *panel_info = mfd->panel_info; int i, lcount = 0; struct mdp_blit_req *req; struct bpp_info bpp; u32 src_read_bw = 0; u32 dst_read_bw = 0; u32 dst_write_bw = 0; u64 honest_ppp_ab = 0; u32 fps; lcount = lreq->count; if (lcount == 0) { pr_err("Blit with request count 0, continue to recover!!!\n"); return 0; } /* Set FPS to mipi rate as currently there is no way to get this */ fps = panel_info->mipi.frame_rate; for (i = 0; i < lcount; i++) { req = &(lreq->req_list[i]); mdp3_get_bpp_info(req->src.format, &bpp); src_read_bw = req->src_rect.w * req->src_rect.h * bpp.bpp_num / bpp.bpp_den; src_read_bw = mdp3_adjust_scale_factor(req, src_read_bw, bpp.bpp_pln); mdp3_get_bpp_info(req->dst.format, &bpp); dst_read_bw = req->dst_rect.w * req->dst_rect.h * bpp.bpp_num / bpp.bpp_den; dst_read_bw = mdp3_adjust_scale_factor(req, dst_read_bw, bpp.bpp_pln); dst_write_bw = req->dst_rect.w * req->dst_rect.h * bpp.bpp_num / bpp.bpp_den; honest_ppp_ab += (src_read_bw + dst_read_bw + dst_write_bw); } honest_ppp_ab = honest_ppp_ab * fps; if (honest_ppp_ab != ppp_res.next_ab) { pr_debug("bandwidth vote update for ppp: ab = %llx\n", honest_ppp_ab); ppp_res.next_ab = honest_ppp_ab; ppp_res.next_ib = honest_ppp_ab; ppp_stat->bw_update = true; } ppp_res.clk_rate = mdp3_clk_calc(mfd, lreq); return 0; } int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off) { uint64_t ab = 0, ib = 0; int rate = 0; int rc; if (on_off) { rate = ppp_res.clk_rate; ab = ppp_res.next_ab; ib = ppp_res.next_ib; } mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, rate, MDP3_CLIENT_PPP); rc = mdp3_res_update(on_off, 0, MDP3_CLIENT_PPP); if (rc < 0) { pr_err("%s: mdp3_clk_enable failed\n", __func__); return rc; } rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib); if (rc < 0) { mdp3_res_update(!on_off, 0, MDP3_CLIENT_PPP); pr_err("%s: scale_set_quota failed\n", __func__); return rc; } ppp_stat->bw_on = on_off; ppp_stat->mdp_clk = MDP_CORE_CLK_RATE_SVS; ppp_stat->bw_update = false; return 0; } void mdp3_start_ppp(struct ppp_blit_op *blit_op) { /* Wait for the pipe to clear */ if (MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS) & MDP3_PPP_ACTIVE) { pr_err("ppp core is hung up on previous request\n"); return; } config_ppp_op_mode(blit_op); if (blit_op->solid_fill) { MDP3_REG_WRITE(0x10138, 0x10000000); MDP3_REG_WRITE(0x1014c, 0xffffffff); MDP3_REG_WRITE(0x101b8, 0); MDP3_REG_WRITE(0x101bc, 0); MDP3_REG_WRITE(0x1013c, 0); MDP3_REG_WRITE(0x10140, 0); MDP3_REG_WRITE(0x10144, 0); MDP3_REG_WRITE(0x10148, 0); MDP3_REG_WRITE(MDP3_TFETCH_FILL_COLOR, blit_op->solid_fill_color); MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL, ENABLE_SOLID_FILL); } else { MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL, DISABLE_SOLID_FILL); } mdp3_ppp_kickoff(); } static int solid_fill_workaround(struct mdp_blit_req *req, struct ppp_blit_op *blit_op) { /* Make width 2 when there is a solid fill of width 1, and make sure width does not become zero while trying to avoid odd width */ if (blit_op->dst.roi.width == 1) { if (req->dst_rect.x + 2 > req->dst.width) { pr_err("%s: Unable to handle solid fill of width 1", __func__); return -EINVAL; } blit_op->dst.roi.width = 2; } if (blit_op->src.roi.width == 1) { if (req->src_rect.x + 2 > req->src.width) { pr_err("%s: Unable to handle solid fill of width 1", __func__); return -EINVAL; } blit_op->src.roi.width = 2; } /* Avoid odd width, as it could hang ppp during solid fill */ blit_op->dst.roi.width = (blit_op->dst.roi.width / 2) * 2; blit_op->src.roi.width = (blit_op->src.roi.width / 2) * 2; /* Avoid RGBA format, as it could hang ppp during solid fill */ if (blit_op->src.color_fmt == MDP_RGBA_8888) blit_op->src.color_fmt = MDP_RGBX_8888; if (blit_op->dst.color_fmt == MDP_RGBA_8888) blit_op->dst.color_fmt = MDP_RGBX_8888; return 0; } static int mdp3_ppp_process_req(struct ppp_blit_op *blit_op, struct mdp_blit_req *req, struct mdp3_img_data *src_data, struct mdp3_img_data *dst_data) { unsigned long srcp0_start, srcp0_len, dst_start, dst_len; uint32_t dst_width, dst_height; int ret = 0; srcp0_start = (unsigned long) src_data->addr; srcp0_len = (unsigned long) src_data->len; dst_start = (unsigned long) dst_data->addr; dst_len = (unsigned long) dst_data->len; blit_op->dst.prop.width = req->dst.width; blit_op->dst.prop.height = req->dst.height; blit_op->dst.color_fmt = req->dst.format; blit_op->dst.p0 = (void *) dst_start; blit_op->dst.p0 += req->dst.offset; blit_op->dst.roi.x = req->dst_rect.x; blit_op->dst.roi.y = req->dst_rect.y; blit_op->dst.roi.width = req->dst_rect.w; blit_op->dst.roi.height = req->dst_rect.h; blit_op->src.roi.x = req->src_rect.x; blit_op->src.roi.y = req->src_rect.y; blit_op->src.roi.width = req->src_rect.w; blit_op->src.roi.height = req->src_rect.h; blit_op->src.prop.width = req->src.width; blit_op->src.color_fmt = req->src.format; blit_op->src.p0 = (void *) (srcp0_start + req->src.offset); if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO) blit_op->src.p1 = (void *) ((uint32_t) blit_op->src.p0 + ALIGN((ALIGN(req->src.width, 32) * ALIGN(req->src.height, 32)), 4096)); else if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_VENUS) blit_op->src.p1 = (void *) ((uint32_t) blit_op->src.p0 + ALIGN((ALIGN(req->src.width, 128) * ALIGN(req->src.height, 32)), 4096)); else blit_op->src.p1 = (void *) ((uint32_t) blit_op->src.p0 + req->src.width * req->src.height); if (req->flags & MDP_IS_FG) blit_op->mdp_op |= MDPOP_LAYER_IS_FG; /* blending check */ if (req->transp_mask != MDP_TRANSP_NOP) { blit_op->mdp_op |= MDPOP_TRANSP; blit_op->blend.trans_color = mdp3_calc_tpval(&blit_op->src, req->transp_mask); } else { blit_op->blend.trans_color = 0; } req->alpha &= 0xff; if (req->alpha < MDP_ALPHA_NOP) { blit_op->mdp_op |= MDPOP_ALPHAB; blit_op->blend.const_alpha = req->alpha; } else { blit_op->blend.const_alpha = 0xff; } /* rotation check */ if (req->flags & MDP_FLIP_LR) blit_op->mdp_op |= MDPOP_LR; if (req->flags & MDP_FLIP_UD) blit_op->mdp_op |= MDPOP_UD; if (req->flags & MDP_ROT_90) blit_op->mdp_op |= MDPOP_ROT90; if (req->flags & MDP_DITHER) blit_op->mdp_op |= MDPOP_DITHER; if (req->flags & MDP_BLEND_FG_PREMULT) blit_op->mdp_op |= MDPOP_FG_PM_ALPHA; /* scale check */ if (req->flags & MDP_ROT_90) { dst_width = req->dst_rect.h; dst_height = req->dst_rect.w; } else { dst_width = req->dst_rect.w; dst_height = req->dst_rect.h; } if ((blit_op->src.roi.width != dst_width) || (blit_op->src.roi.height != dst_height)) blit_op->mdp_op |= MDPOP_ASCALE; if (req->flags & MDP_BLUR) blit_op->mdp_op |= MDPOP_ASCALE | MDPOP_BLUR; if (req->flags & MDP_SOLID_FILL) { ret = solid_fill_workaround(req, blit_op); if (ret) return ret; blit_op->solid_fill_color = (req->const_color.g & 0xFF)| (req->const_color.r & 0xFF) << 8 | (req->const_color.b & 0xFF) << 16 | (req->const_color.alpha & 0xFF) << 24; blit_op->solid_fill = true; } else { blit_op->solid_fill = false; } return ret; } static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op, struct mdp_blit_req *req) { int dst_h, src_w, i; uint32_t mdp_op = blit_op->mdp_op; void *src_p0 = blit_op->src.p0; void *src_p1 = blit_op->src.p1; void *dst_p0 = blit_op->dst.p0; src_w = req->src_rect.w; dst_h = blit_op->dst.roi.height; /* bg tile fetching HW workaround */ for (i = 0; i < (req->dst_rect.h / 16); i++) { /* this tile size */ blit_op->dst.roi.height = 16; blit_op->src.roi.width = (16 * req->src_rect.w) / req->dst_rect.h; /* if it's out of scale range... */ if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) blit_op->src.roi.width = (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / MDP_MAX_X_SCALE_FACTOR; else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) blit_op->src.roi.width = (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / MDP_MIN_X_SCALE_FACTOR; mdp3_start_ppp(blit_op); /* next tile location */ blit_op->dst.roi.y += 16; blit_op->src.roi.x += blit_op->src.roi.width; /* this is for a remainder update */ dst_h -= 16; src_w -= blit_op->src.roi.width; /* restore parameters that may have been overwritten */ blit_op->mdp_op = mdp_op; blit_op->src.p0 = src_p0; blit_op->src.p1 = src_p1; blit_op->dst.p0 = dst_p0; } if ((dst_h < 0) || (src_w < 0)) pr_err ("msm_fb: mdp_blt_ex() unexpected result! line:%d\n", __LINE__); /* remainder update */ if ((dst_h > 0) && (src_w > 0)) { u32 tmp_v; blit_op->dst.roi.height = dst_h; blit_op->src.roi.width = src_w; if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) { tmp_v = (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / MDP_MAX_X_SCALE_FACTOR + ((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) % MDP_MAX_X_SCALE_FACTOR ? 1 : 0); /* move x location as roi width gets bigger */ blit_op->src.roi.x -= tmp_v - blit_op->src.roi.width; blit_op->src.roi.width = tmp_v; } else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) { tmp_v = (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) / MDP_MIN_X_SCALE_FACTOR + ((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) % MDP_MIN_X_SCALE_FACTOR ? 1 : 0); /* * we don't move x location for continuity of * source image */ blit_op->src.roi.width = tmp_v; } mdp3_start_ppp(blit_op); } } static int mdp3_ppp_blit(struct msm_fb_data_type *mfd, struct mdp_blit_req *req, struct mdp3_img_data *src_data, struct mdp3_img_data *dst_data) { struct ppp_blit_op blit_op; int ret = 0; memset(&blit_op, 0, sizeof(blit_op)); if (req->dst.format == MDP_FB_FORMAT) req->dst.format = mfd->fb_imgType; if (req->src.format == MDP_FB_FORMAT) req->src.format = mfd->fb_imgType; if (mdp3_ppp_verify_req(req)) { pr_err("%s: invalid image!\n", __func__); return -EINVAL; } ret = mdp3_ppp_process_req(&blit_op, req, src_data, dst_data); if (ret) { pr_err("%s: Failed to process the blit request", __func__); return ret; } if (((blit_op.mdp_op & (MDPOP_TRANSP | MDPOP_ALPHAB)) || (req->src.format == MDP_ARGB_8888) || (req->src.format == MDP_BGRA_8888) || (req->src.format == MDP_RGBA_8888)) && (blit_op.mdp_op & MDPOP_ROT90) && (req->dst_rect.w <= 16)) { mdp3_ppp_tile_workaround(&blit_op, req); } else { mdp3_start_ppp(&blit_op); } return 0; } static int mdp3_ppp_blit_workaround(struct msm_fb_data_type *mfd, struct mdp_blit_req *req, unsigned int remainder, struct mdp3_img_data *src_data, struct mdp3_img_data *dst_data) { 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; /* make new request as provide by user */ 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 (remainder == 14 || remainder == 6) d_w_1 = req->dst_rect.w / 2; 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; /* blit first region */ if (((splitreq.flags & 0x07) == 0x07) || ((splitreq.flags & 0x07) == 0x05) || ((splitreq.flags & 0x07) == 0x02) || ((splitreq.flags & 0x07) == 0x0)) { 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--; } } 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 { 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_0) / 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_0 >= 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_0) / 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_0 >= 8 * s_w_1) { s_w_1++; s_x_1--; } } 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; } /* No need to split in height */ ret = mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data); if (ret) return ret; /* blit second region */ if (((splitreq.flags & 0x07) == 0x07) || ((splitreq.flags & 0x07) == 0x05) || ((splitreq.flags & 0x07) == 0x02) || ((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; } /* No need to split in height ... just width */ return mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data); } int mdp3_ppp_start_blit(struct msm_fb_data_type *mfd, struct mdp_blit_req *req, struct mdp3_img_data *src_data, struct mdp3_img_data *dst_data) { int ret; unsigned int remainder = 0, is_bpp_4 = 0; if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { pr_err("mdp_ppp: src img of zero size!\n"); return -EINVAL; } if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0)) return 0; /* MDP width split workaround */ remainder = (req->dst_rect.w) % 16; ret = ppp_get_bpp(req->dst.format, mfd->fb_imgType); if (ret <= 0) { pr_err("mdp_ppp: incorrect bpp!\n"); return -EINVAL; } is_bpp_4 = (ret == 4) ? 1 : 0; if ((is_bpp_4 && (remainder == 6 || remainder == 14)) && !(req->flags & MDP_SOLID_FILL)) ret = mdp3_ppp_blit_workaround(mfd, req, remainder, src_data, dst_data); else ret = mdp3_ppp_blit(mfd, req, src_data, dst_data); return ret; } void mdp3_ppp_wait_for_fence(struct blit_req_list *req) { int i, ret = 0; /* buf sync */ for (i = 0; i < req->acq_fen_cnt; i++) { ret = sync_fence_wait(req->acq_fen[i], WAIT_FENCE_FINAL_TIMEOUT); if (ret < 0) { pr_err("%s: sync_fence_wait failed! ret = %x\n", __func__, ret); break; } sync_fence_put(req->acq_fen[i]); } if (ret < 0) { while (i < req->acq_fen_cnt) { sync_fence_put(req->acq_fen[i]); i++; } } req->acq_fen_cnt = 0; } void mdp3_ppp_signal_timeline(struct blit_req_list *req) { sw_sync_timeline_inc(ppp_stat->timeline, 1); req->last_rel_fence = req->cur_rel_fence; req->cur_rel_fence = 0; } static void mdp3_ppp_deinit_buf_sync(struct blit_req_list *req) { int i; put_unused_fd(req->cur_rel_fen_fd); sync_fence_put(req->cur_rel_fence); req->cur_rel_fence = NULL; req->cur_rel_fen_fd = 0; ppp_stat->timeline_value--; for (i = 0; i < req->acq_fen_cnt; i++) sync_fence_put(req->acq_fen[i]); req->acq_fen_cnt = 0; } static int mdp3_ppp_handle_buf_sync(struct blit_req_list *req, struct mdp_buf_sync *buf_sync) { int i, fence_cnt = 0, ret = 0; int acq_fen_fd[MDP_MAX_FENCE_FD]; struct sync_fence *fence; if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) || (ppp_stat->timeline == NULL)) return -EINVAL; if (buf_sync->acq_fen_fd_cnt) ret = copy_from_user(acq_fen_fd, buf_sync->acq_fen_fd, buf_sync->acq_fen_fd_cnt * sizeof(int)); if (ret) { pr_err("%s: copy_from_user failed\n", __func__); return ret; } for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) { fence = sync_fence_fdget(acq_fen_fd[i]); if (fence == NULL) { pr_info("%s: null fence! i=%d fd=%d\n", __func__, i, acq_fen_fd[i]); ret = -EINVAL; break; } req->acq_fen[i] = fence; } fence_cnt = i; if (ret) goto buf_sync_err_1; req->acq_fen_cnt = fence_cnt; if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT) mdp3_ppp_wait_for_fence(req); req->cur_rel_sync_pt = sw_sync_pt_create(ppp_stat->timeline, ppp_stat->timeline_value++); if (req->cur_rel_sync_pt == NULL) { pr_err("%s: cannot create sync point\n", __func__); ret = -ENOMEM; goto buf_sync_err_2; } /* create fence */ req->cur_rel_fence = sync_fence_create("ppp-fence", req->cur_rel_sync_pt); if (req->cur_rel_fence == NULL) { sync_pt_free(req->cur_rel_sync_pt); req->cur_rel_sync_pt = NULL; pr_err("%s: cannot create fence\n", __func__); ret = -ENOMEM; goto buf_sync_err_2; } /* create fd */ return ret; buf_sync_err_2: ppp_stat->timeline_value--; buf_sync_err_1: for (i = 0; i < fence_cnt; i++) sync_fence_put(req->acq_fen[i]); req->acq_fen_cnt = 0; return ret; } void mdp3_ppp_req_push(struct blit_req_queue *req_q, struct blit_req_list *req) { int idx = req_q->push_idx; req_q->req[idx] = *req; req_q->count++; req_q->push_idx = (req_q->push_idx + 1) % MDP3_PPP_MAX_LIST_REQ; } struct blit_req_list *mdp3_ppp_next_req(struct blit_req_queue *req_q) { struct blit_req_list *req; if (req_q->count == 0) return NULL; req = &req_q->req[req_q->pop_idx]; return req; } void mdp3_ppp_req_pop(struct blit_req_queue *req_q) { req_q->count--; req_q->pop_idx = (req_q->pop_idx + 1) % MDP3_PPP_MAX_LIST_REQ; } void mdp3_free_fw_timer_func(unsigned long arg) { schedule_work(&ppp_stat->free_bw_work); } static void mdp3_free_bw_wq_handler(struct work_struct *work) { struct msm_fb_data_type *mfd = ppp_stat->mfd; mutex_lock(&ppp_stat->config_ppp_mutex); if (ppp_stat->bw_on) { mdp3_ppp_turnon(mfd, 0); } mutex_unlock(&ppp_stat->config_ppp_mutex); } static void mdp3_ppp_blit_wq_handler(struct work_struct *work) { struct msm_fb_data_type *mfd = ppp_stat->mfd; struct blit_req_list *req; int i, rc = 0; mutex_lock(&ppp_stat->config_ppp_mutex); req = mdp3_ppp_next_req(&ppp_stat->req_q); if (!req) { mutex_unlock(&ppp_stat->config_ppp_mutex); return; } if (!ppp_stat->bw_on) { mdp3_ppp_turnon(mfd, 1); if (rc < 0) { mutex_unlock(&ppp_stat->config_ppp_mutex); pr_err("%s: Enable ppp resources failed\n", __func__); return; } } while (req) { mdp3_ppp_wait_for_fence(req); mdp3_calc_ppp_res(mfd, req); if (ppp_res.clk_rate != ppp_stat->mdp_clk) { ppp_stat->mdp_clk = ppp_res.clk_rate; mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, ppp_stat->mdp_clk, MDP3_CLIENT_PPP); } if (ppp_stat->bw_update) { rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ppp_res.next_ab, ppp_res.next_ib); if (rc < 0) { pr_err("%s: bw set quota failed\n", __func__); return; } ppp_stat->bw_update = false; } for (i = 0; i < req->count; i++) { if (!(req->req_list[i].flags & MDP_NO_BLIT)) { /* Do the actual blit. */ if (!rc) { rc = mdp3_ppp_start_blit(mfd, &(req->req_list[i]), &req->src_data[i], &req->dst_data[i]); } mdp3_put_img(&req->src_data[i]); mdp3_put_img(&req->dst_data[i]); } } /* Signal to release fence */ mutex_lock(&ppp_stat->req_mutex); mdp3_ppp_signal_timeline(req); mdp3_ppp_req_pop(&ppp_stat->req_q); req = mdp3_ppp_next_req(&ppp_stat->req_q); if (ppp_stat->wait_for_pop) complete(&ppp_stat->pop_q_comp); mutex_unlock(&ppp_stat->req_mutex); } mod_timer(&ppp_stat->free_bw_timer, jiffies + msecs_to_jiffies(MDP_RELEASE_BW_TIMEOUT)); mutex_unlock(&ppp_stat->config_ppp_mutex); } int mdp3_ppp_parse_req(void __user *p, struct mdp_async_blit_req_list *req_list_header, int async) { struct blit_req_list *req; struct blit_req_queue *req_q = &ppp_stat->req_q; struct sync_fence *fence = NULL; int count, rc, idx, i; count = req_list_header->count; mutex_lock(&ppp_stat->req_mutex); while (req_q->count >= MDP3_PPP_MAX_LIST_REQ) { ppp_stat->wait_for_pop = true; mutex_unlock(&ppp_stat->req_mutex); rc = wait_for_completion_timeout( &ppp_stat->pop_q_comp, 5 * HZ); if (rc == 0) { /* This will only occur if there is serious problem */ pr_err("%s: timeout exiting queuing request\n", __func__); return -EBUSY; } mutex_lock(&ppp_stat->req_mutex); ppp_stat->wait_for_pop = false; } idx = req_q->push_idx; req = &req_q->req[idx]; if (copy_from_user(&req->req_list, p, sizeof(struct mdp_blit_req) * count)) { mutex_unlock(&ppp_stat->req_mutex); return -EFAULT; } rc = mdp3_ppp_handle_buf_sync(req, &req_list_header->sync); if (rc < 0) { pr_err("%s: Failed create sync point\n", __func__); mutex_unlock(&ppp_stat->req_mutex); return rc; } req->count = count; /* We need to grab ion handle while running in client thread */ for (i = 0; i < count; i++) { rc = mdp3_ppp_get_img(&req->req_list[i].src, &req->req_list[i], &req->src_data[i]); if (rc < 0 || req->src_data[i].len == 0) { pr_err("mdp_ppp: couldn't retrieve src img from mem\n"); goto parse_err_1; } rc = mdp3_ppp_get_img(&req->req_list[i].dst, &req->req_list[i], &req->dst_data[i]); if (rc < 0 || req->dst_data[i].len == 0) { mdp3_put_img(&req->src_data[i]); pr_err("mdp_ppp: couldn't retrieve dest img from mem\n"); goto parse_err_1; } } if (async) { req->cur_rel_fen_fd = get_unused_fd_flags(0); if (req->cur_rel_fen_fd < 0) { pr_err("%s: get_unused_fd_flags failed\n", __func__); rc = -ENOMEM; goto parse_err_1; } sync_fence_install(req->cur_rel_fence, req->cur_rel_fen_fd); rc = copy_to_user(req_list_header->sync.rel_fen_fd, &req->cur_rel_fen_fd, sizeof(int)); if (rc) { pr_err("%s:copy_to_user failed\n", __func__); goto parse_err_2; } } else { fence = req->cur_rel_fence; } mdp3_ppp_req_push(req_q, req); mutex_unlock(&ppp_stat->req_mutex); schedule_work(&ppp_stat->blit_work); if (!async) { /* wait for release fence */ rc = sync_fence_wait(fence, 5 * MSEC_PER_SEC); if (rc < 0) pr_err("%s: sync blit! rc = %x\n", __func__, rc); sync_fence_put(fence); fence = NULL; } return 0; parse_err_2: put_unused_fd(req->cur_rel_fen_fd); parse_err_1: for (i--; i >= 0; i--) { mdp3_put_img(&req->src_data[i]); mdp3_put_img(&req->dst_data[i]); } mdp3_ppp_deinit_buf_sync(req); mutex_unlock(&ppp_stat->req_mutex); return rc; } int mdp3_ppp_res_init(struct msm_fb_data_type *mfd) { const char timeline_name[] = "mdp3_ppp"; ppp_stat = kzalloc(sizeof(struct ppp_status), GFP_KERNEL); if (!ppp_stat) { pr_err("%s: kzalloc failed\n", __func__); return -ENOMEM; } /*Setup sync_pt timeline for ppp*/ ppp_stat->timeline = sw_sync_timeline_create(timeline_name); if (ppp_stat->timeline == NULL) { pr_err("%s: cannot create time line\n", __func__); return -ENOMEM; } else { ppp_stat->timeline_value = 1; } INIT_WORK(&ppp_stat->blit_work, mdp3_ppp_blit_wq_handler); INIT_WORK(&ppp_stat->free_bw_work, mdp3_free_bw_wq_handler); init_completion(&ppp_stat->pop_q_comp); mutex_init(&ppp_stat->req_mutex); mutex_init(&ppp_stat->config_ppp_mutex); init_timer(&ppp_stat->free_bw_timer); ppp_stat->free_bw_timer.function = mdp3_free_fw_timer_func; ppp_stat->free_bw_timer.data = 0; ppp_stat->mfd = mfd; mdp3_ppp_callback_setup(); return 0; }