/* * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. * */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include "mdss_fb.h" #include "mdss_mdp.h" #include "mdss_mdp_pp.h" #include "mdss_mdp_pp_common.h" #define IGC_DSPP_OP_MODE_EN BIT(0) #define ENHIST_BIT_SHIFT 16 /* PA related define */ /* Offsets from DSPP/VIG base to PA block */ #define PA_DSPP_BLOCK_REG_OFF 0x800 #define PA_VIG_BLOCK_REG_OFF 0x1200 /* Offsets to various subblocks from PA block * in VIG/DSPP. */ #define PA_OP_MODE_REG_OFF 0x0 #define PA_HIST_REG_OFF 0x4 #define PA_LUTV_SWAP_REG_OFF 0x18 #define PA_HSIC_REG_OFF 0x1C #define PA_DITHER_CTL_REG_OFF 0x2C #define PA_PWL_HOLD_REG_OFF 0x40 /* Memory Color offsets */ #define PA_MEM_COL_REG_OFF 0x80 #define PA_MEM_SKIN_REG_OFF (PA_MEM_COL_REG_OFF) #define PA_MEM_SKY_REG_OFF (PA_MEM_SKIN_REG_OFF + \ JUMP_REGISTERS_OFF(5)) #define PA_MEM_FOL_REG_OFF (PA_MEM_SKY_REG_OFF + \ JUMP_REGISTERS_OFF(5)) #define PA_MEM_SKIN_ADJUST_P2_REG_OFF (PA_MEM_FOL_REG_OFF + \ JUMP_REGISTERS_OFF(5)) #define PA_MEM_SKY_ADJUST_P2_REG_OFF (PA_MEM_SKIN_ADJUST_P2_REG_OFF + \ JUMP_REGISTERS_OFF(2)) #define PA_MEM_FOL_ADJUST_P2_REG_OFF (PA_MEM_SKY_ADJUST_P2_REG_OFF + \ JUMP_REGISTERS_OFF(2)) #define PA_SZONE_REG_OFF 0x100 #define PA_LUTV_REG_OFF 0x200 #define PA_HIST_RAM_REG_OFF 0x400 #define PPB_GLOBAL_DITHER_REG_OFF 0x30E0 #define DITHER_MATRIX_LEN 16 #define DITHER_DEPTH_MAP_INDEX 9 static u32 dither_matrix[DITHER_MATRIX_LEN] = { 15, 7, 13, 5, 3, 11, 1, 9, 12, 4, 14, 6, 0, 8, 2, 10}; static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = { 0, 0, 0, 0, 0, 1, 2, 3, 3}; #define PA_DITHER_REG_OFF 0x2C #define IGC_DITHER_STRENGTH_REG_OFF 0x7E0 /* histogram prototypes */ static int pp_get_hist_offset(u32 block, u32 *ctl_off); static int pp_hist_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); static int pp_hist_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num); /* PA LUT prototypes */ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num); static int pp_hist_lut_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); static int pp_hist_lut_get_version(u32 *version); static void pp_hist_lut_opmode_config(char __iomem *base_addr, struct pp_sts_type *pp_sts); /* dither prototypes */ static int pp_dither_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num); static int pp_dither_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); static int pp_dither_get_version(u32 *version); /* PA prototypes */ static int pp_pa_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); static int pp_pa_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num); static int pp_pa_get_version(u32 *version); static void pp_pa_set_global_adj_regs(char __iomem *base_addr, struct mdp_pa_data_v1_7 *pa_data, u32 flag); static void pp_pa_set_mem_col(char __iomem *base_addr, struct mdp_pa_data_v1_7 *pa_data, u32 flags); static void pp_pa_set_six_zone(char __iomem *base_addr, struct mdp_pa_data_v1_7 *pa_data, u32 flags); static void pp_pa_opmode_config(char __iomem *base_addr, struct pp_sts_type *pp_sts); /* PA dither prototypes */ static int pp_pa_dither_get_version(u32 *version); static int pp_pa_dither_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); /* IGC prototypes */ static int pp_igc_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); static int pp_igc_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num); static int pp_igc_get_version(u32 *version); static int pp_igc_dither_set_strength(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type); static void pp_opmode_config(int location, struct pp_sts_type *pp_sts, u32 *opmode, int side); static int pp_driver_init(struct mdp_pp_driver_ops *ops); static struct mdss_pp_res_type_v3 config_data; static int pp_driver_init(struct mdp_pp_driver_ops *ops) { int i = 0; if (!ops->pp_ops[IGC].pp_set_config) { pr_err("IGC function is not set\n"); return -EINVAL; } config_data.igc_set_config = ops->pp_ops[IGC].pp_set_config; for (i = 0; i < MDSS_BLOCK_DISP_NUM; i++) { config_data.igc_v3_data[i].c0_c1_data = &config_data.igc_table_c0_c1[i][0]; config_data.igc_v3_data[i].c2_data = &config_data.igc_table_c2[i][0]; config_data.igc_v3_data[i].len = IGC_LUT_ENTRIES; config_data.igc_v3_data[i].strength = 0; config_data.igc_v3_data[i].table_fmt = mdp_igc_rec_max; } return 0; } void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops) { if (!ops) { pr_err("PP driver ops invalid %pK\n", ops); return ERR_PTR(-EINVAL); } if (pp_driver_init(ops)) return ERR_PTR(-EINVAL); /* PA ops */ ops->pp_ops[PA].pp_set_config = pp_pa_set_config; ops->pp_ops[PA].pp_get_config = pp_pa_get_config; ops->pp_ops[PA].pp_get_version = pp_pa_get_version; /* HIST_LUT ops */ ops->pp_ops[HIST_LUT].pp_set_config = pp_hist_lut_set_config; ops->pp_ops[HIST_LUT].pp_get_config = pp_hist_lut_get_config; ops->pp_ops[HIST_LUT].pp_get_version = pp_hist_lut_get_version; /* HIST ops */ ops->pp_ops[HIST].pp_set_config = pp_hist_set_config; ops->pp_ops[HIST].pp_get_config = pp_hist_get_config; ops->pp_ops[HIST].pp_get_version = NULL; /* Dither ops */ ops->pp_ops[DITHER].pp_set_config = pp_dither_set_config; ops->pp_ops[DITHER].pp_get_config = pp_dither_get_config; ops->pp_ops[DITHER].pp_get_version = pp_dither_get_version; ops->pp_ops[PA_DITHER].pp_get_version = pp_pa_dither_get_version; ops->pp_ops[PA_DITHER].pp_set_config = pp_pa_dither_set_config; ops->pp_ops[PA_DITHER].pp_get_config = NULL; ops->pp_ops[IGC].pp_get_config = pp_igc_get_config; ops->pp_ops[IGC].pp_get_version = pp_igc_get_version; ops->pp_ops[IGC].pp_set_config = pp_igc_set_config; /* Set opmode pointers */ ops->pp_opmode_config = pp_opmode_config; ops->get_hist_offset = pp_get_hist_offset; ops->gamut_clk_gate_en = NULL; ops->igc_set_dither_strength = pp_igc_dither_set_strength; return &config_data; } static int pp_get_hist_offset(u32 block, u32 *ctl_off) { int ret = 0; if (!ctl_off) { pr_err("invalid params ctl_off %pK\n", ctl_off); return -EINVAL; } switch (block) { case SSPP_VIG: *ctl_off = PA_VIG_BLOCK_REG_OFF + PA_HIST_REG_OFF; break; case DSPP: *ctl_off = PA_DSPP_BLOCK_REG_OFF + PA_HIST_REG_OFF; break; default: pr_err("Invalid block type %d\n", block); ret = -EINVAL; break; } return ret; } static int pp_hist_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { u32 opmode = 0; struct pp_hist_col_info *hist_info = NULL; if (!base_addr || !cfg_data || !pp_sts) { pr_err("invalid params base_addr %pK cfg_data %pK pp_sts_type %pK\n", base_addr, cfg_data, pp_sts); return -EINVAL; } if (block_type != DSPP) { pr_err("Invalid block type %d\n", block_type); return -EINVAL; } hist_info = (struct pp_hist_col_info *)cfg_data; opmode = readl_relaxed(base_addr + PA_DSPP_BLOCK_REG_OFF + PA_OP_MODE_REG_OFF); /* set the hist_en bit */ if (hist_info->col_en) { pp_sts->hist_sts |= PP_STS_ENABLE; opmode |= BIT(16); } else { pp_sts->hist_sts &= ~PP_STS_ENABLE; opmode &= ~BIT(16); } writel_relaxed(opmode, base_addr + PA_DSPP_BLOCK_REG_OFF + PA_OP_MODE_REG_OFF); return 0; } static int pp_hist_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num) { int i = 0; u32 sum = 0; struct pp_hist_col_info *hist_info = NULL; char __iomem *hist_addr; if (!base_addr || !cfg_data) { pr_err("invalid params base_addr %pK cfg_data %pK\n", base_addr, cfg_data); return -EINVAL; } if (block_type != DSPP) { pr_err("Invalid block type %d\n", block_type); return -EINVAL; } hist_info = (struct pp_hist_col_info *) cfg_data; hist_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_HIST_RAM_REG_OFF; for (i = 0; i < HIST_V_SIZE; i++) { hist_info->data[i] = readl_relaxed(hist_addr) & REG_MASK(24); hist_addr += 0x4; sum += hist_info->data[i]; } hist_info->hist_cnt_read++; return sum; } static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num) { int ret = 0, i = 0; char __iomem *hist_lut_addr; u32 sz = 0, temp = 0, *data = NULL; struct mdp_hist_lut_data_v1_7 lut_data_v1_7; struct mdp_hist_lut_data_v1_7 *lut_data = &lut_data_v1_7; struct mdp_hist_lut_data *lut_cfg_data = NULL; if (!base_addr || !cfg_data) { pr_err("invalid params base_addr %pK cfg_data %pK\n", base_addr, cfg_data); return -EINVAL; } if (block_type != DSPP) { pr_err("Invalid block type %d\n", block_type); return -EINVAL; } lut_cfg_data = (struct mdp_hist_lut_data *) cfg_data; if (!(lut_cfg_data->ops & MDP_PP_OPS_READ)) { pr_err("read ops not set for hist_lut %d\n", lut_cfg_data->ops); return 0; } if (lut_cfg_data->version != mdp_hist_lut_v1_7 || !lut_cfg_data->cfg_payload) { pr_err("invalid hist_lut version %d payload %pK\n", lut_cfg_data->version, lut_cfg_data->cfg_payload); return -EINVAL; } if (copy_from_user(lut_data, (void __user *) lut_cfg_data->cfg_payload, sizeof(*lut_data))) { pr_err("copy from user failed for lut_data\n"); return -EFAULT; } if (lut_data->len != ENHIST_LUT_ENTRIES) { pr_err("invalid hist_lut len %d", lut_data->len); return -EINVAL; } sz = ENHIST_LUT_ENTRIES * sizeof(u32); if (!access_ok(VERIFY_WRITE, lut_data->data, sz)) { pr_err("invalid lut address for hist_lut sz %d\n", sz); return -EFAULT; } hist_lut_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_LUTV_REG_OFF; data = kzalloc(sz, GFP_KERNEL); if (!data) return -ENOMEM; for (i = 0; i < ENHIST_LUT_ENTRIES; i += 2) { temp = readl_relaxed(hist_lut_addr); data[i] = temp & REG_MASK(10); data[i + 1] = (temp & REG_MASK_SHIFT(10, 16)) >> ENHIST_BIT_SHIFT; hist_lut_addr += 4; } if (copy_to_user(lut_data->data, data, sz)) { pr_err("failed to copy the hist_lut back to user\n"); ret = -EFAULT; } kfree(data); return ret; } static int pp_hist_lut_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { int ret = 0, i = 0; u32 temp = 0; struct mdp_hist_lut_data *lut_cfg_data = NULL; struct mdp_hist_lut_data_v1_7 *lut_data = NULL; char __iomem *hist_lut_addr = NULL, *swap_addr = NULL; if (!base_addr || !cfg_data || !pp_sts) { pr_err("invalid params base_addr %pK cfg_data %pK pp_sts_type %pK\n", base_addr, cfg_data, pp_sts); return -EINVAL; } if (block_type != DSPP) { pr_err("Invalid block type %d\n", block_type); return -EINVAL; } lut_cfg_data = (struct mdp_hist_lut_data *) cfg_data; if (lut_cfg_data->version != mdp_hist_lut_v1_7) { pr_err("invalid hist_lut version %d\n", lut_cfg_data->version); return -EINVAL; } if (!(lut_cfg_data->ops & ~(MDP_PP_OPS_READ))) { pr_err("only read ops set for lut\n"); return ret; } if (lut_cfg_data->ops & MDP_PP_OPS_DISABLE || !(lut_cfg_data->ops & MDP_PP_OPS_WRITE)) { pr_debug("non write ops set %d\n", lut_cfg_data->ops); goto hist_lut_set_sts; } lut_data = lut_cfg_data->cfg_payload; if (!lut_data) { pr_err("invalid hist_lut cfg_payload %pK\n", lut_data); return -EINVAL; } if (lut_data->len != ENHIST_LUT_ENTRIES || !lut_data->data) { pr_err("invalid hist_lut len %d data %pK\n", lut_data->len, lut_data->data); return -EINVAL; } hist_lut_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_LUTV_REG_OFF; swap_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_LUTV_SWAP_REG_OFF; for (i = 0; i < ENHIST_LUT_ENTRIES; i += 2) { temp = (lut_data->data[i] & REG_MASK(10)) | ((lut_data->data[i + 1] & REG_MASK(10)) << ENHIST_BIT_SHIFT); writel_relaxed(temp, hist_lut_addr); hist_lut_addr += 4; } writel_relaxed(1, swap_addr); hist_lut_set_sts: if (lut_cfg_data->ops & MDP_PP_OPS_DISABLE) { pp_sts->enhist_sts &= ~(PP_STS_ENABLE | PP_STS_PA_LUT_FIRST); } else if (lut_cfg_data->ops & MDP_PP_OPS_ENABLE) { pp_sts->enhist_sts |= PP_STS_ENABLE; if (lut_cfg_data->hist_lut_first) pp_sts->enhist_sts |= PP_STS_PA_LUT_FIRST; else pp_sts->enhist_sts &= ~PP_STS_PA_LUT_FIRST; } pp_hist_lut_opmode_config(base_addr + PA_DSPP_BLOCK_REG_OFF, pp_sts); return ret; } static int pp_hist_lut_get_version(u32 *version) { if (!version) { pr_err("invalid param version %pK\n", version); return -EINVAL; } *version = mdp_hist_lut_v1_7; return 0; } static void pp_hist_lut_opmode_config(char __iomem *base_addr, struct pp_sts_type *pp_sts) { u32 opmode = 0; if (!base_addr || !pp_sts) { pr_err("invalid params base_addr %pK pp_sts_type %pK\n", base_addr, pp_sts); return; } opmode = readl_relaxed(base_addr + PA_OP_MODE_REG_OFF); /* set the hist_lutv_en and hist_lutv_first_en bits */ if (pp_sts->enhist_sts & PP_STS_ENABLE) { opmode |= BIT(19) | BIT(20); opmode |= (pp_sts->enhist_sts & PP_STS_PA_LUT_FIRST) ? BIT(21) : 0; } else { opmode &= ~(BIT(19) | BIT(21)); if (!(pp_sts->pa_sts & PP_STS_ENABLE)) opmode &= ~BIT(20); } writel_relaxed(opmode, base_addr + PA_OP_MODE_REG_OFF); } static int pp_pa_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { struct mdp_pa_v2_cfg_data *pa_cfg_data = NULL; struct mdp_pa_data_v1_7 *pa_data = NULL; char __iomem *block_addr = NULL; if (!base_addr || !cfg_data || !pp_sts) { pr_err("invalid params base_addr %pK cfg_data %pK pp_sts_type %pK\n", base_addr, cfg_data, pp_sts); return -EINVAL; } if ((block_type != DSPP) && (block_type != SSPP_VIG)) { pr_err("Invalid block type %d\n", block_type); return -EINVAL; } pa_cfg_data = (struct mdp_pa_v2_cfg_data *) cfg_data; if (pa_cfg_data->version != mdp_pa_v1_7) { pr_err("invalid pa version %d\n", pa_cfg_data->version); return -EINVAL; } if (!(pa_cfg_data->flags & ~(MDP_PP_OPS_READ))) { pr_info("only read ops is set %d", pa_cfg_data->flags); return 0; } block_addr = base_addr + ((block_type == DSPP) ? PA_DSPP_BLOCK_REG_OFF : PA_VIG_BLOCK_REG_OFF); if (pa_cfg_data->flags & MDP_PP_OPS_DISABLE || !(pa_cfg_data->flags & MDP_PP_OPS_WRITE)) { pr_debug("pa_cfg_data->flags = %d\n", pa_cfg_data->flags); goto pa_set_sts; } pa_data = pa_cfg_data->cfg_payload; if (!pa_data) { pr_err("invalid payload for pa %pK\n", pa_data); return -EINVAL; } pp_pa_set_global_adj_regs(block_addr, pa_data, pa_cfg_data->flags); pp_pa_set_mem_col(block_addr, pa_data, pa_cfg_data->flags); if (block_type == DSPP) pp_pa_set_six_zone(block_addr, pa_data, pa_cfg_data->flags); pa_set_sts: pp_pa_set_sts(pp_sts, pa_data, pa_cfg_data->flags, block_type); pp_pa_opmode_config(block_addr, pp_sts); return 0; } static int pp_pa_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num) { return -ENOTSUPP; } static int pp_pa_get_version(u32 *version) { if (!version) { pr_err("invalid param version"); return -EINVAL; } *version = mdp_pa_v1_7; return 0; } static int pp_dither_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num) { return -ENOTSUPP; } static int pp_dither_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { int i = 0; u32 data; struct mdp_dither_cfg_data *dither_cfg_data = NULL; struct mdp_dither_data_v1_7 *dither_data = NULL; char __iomem *dither_opmode = NULL; if (!base_addr || !cfg_data || !pp_sts) { pr_err("invalid params base_addr %pK cfg_data %pK pp_sts_type %pK\n", base_addr, cfg_data, pp_sts); return -EINVAL; } if (block_type != PPB) return -ENOTSUPP; dither_opmode = base_addr + PPB_GLOBAL_DITHER_REG_OFF; base_addr = dither_opmode + 4; dither_cfg_data = (struct mdp_dither_cfg_data *) cfg_data; if (dither_cfg_data->version != mdp_dither_v1_7) { pr_err("invalid dither version %d\n", dither_cfg_data->version); return -EINVAL; } if (dither_cfg_data->flags & MDP_PP_OPS_READ) { pr_err("Invalid context for read operation\n"); return -EINVAL; } if (dither_cfg_data->flags & MDP_PP_OPS_DISABLE || !(dither_cfg_data->flags & MDP_PP_OPS_WRITE)) { pr_debug("non write ops set %d\n", dither_cfg_data->flags); goto dither_set_sts; } dither_data = dither_cfg_data->cfg_payload; if (!dither_data) { pr_err("invalid payload for dither %pK\n", dither_data); return -EINVAL; } if ((dither_data->g_y_depth >= DITHER_DEPTH_MAP_INDEX) || (dither_data->b_cb_depth >= DITHER_DEPTH_MAP_INDEX) || (dither_data->r_cr_depth >= DITHER_DEPTH_MAP_INDEX)) { pr_err("invalid data for dither, g_y_depth %d y_cb_depth %d r_cr_depth %d\n", dither_data->g_y_depth, dither_data->b_cb_depth, dither_data->r_cr_depth); return -EINVAL; } data = dither_depth_map[dither_data->g_y_depth]; data |= dither_depth_map[dither_data->b_cb_depth] << 2; data |= dither_depth_map[dither_data->r_cr_depth] << 4; data |= (dither_data->temporal_en) ? (1 << 8) : 0; writel_relaxed(data, base_addr); base_addr += 4; for (i = 0; i < DITHER_MATRIX_LEN; i += 4) { data = (dither_matrix[i] & REG_MASK(4)) | ((dither_matrix[i + 1] & REG_MASK(4)) << 4) | ((dither_matrix[i + 2] & REG_MASK(4)) << 8) | ((dither_matrix[i + 3] & REG_MASK(4)) << 12); writel_relaxed(data, base_addr); base_addr += 4; } dither_set_sts: pp_sts_set_split_bits(&pp_sts->dither_sts, dither_cfg_data->flags); if (dither_cfg_data->flags & MDP_PP_OPS_DISABLE) { pp_sts->dither_sts &= ~PP_STS_ENABLE; writel_relaxed(0, dither_opmode); } else if (dither_cfg_data->flags & MDP_PP_OPS_ENABLE) { pp_sts->dither_sts |= PP_STS_ENABLE; if (pp_sts_is_enabled(pp_sts->dither_sts, pp_sts->side_sts)) writel_relaxed(BIT(0), dither_opmode); } return 0; } static int pp_dither_get_version(u32 *version) { if (!version) { pr_err("invalid param version"); return -EINVAL; } *version = mdp_dither_v1_7; return 0; } static void pp_opmode_config(int location, struct pp_sts_type *pp_sts, u32 *opmode, int side) { if (!pp_sts || !opmode) { pr_err("Invalid pp_sts %pK or opmode %pK\n", pp_sts, opmode); return; } switch (location) { case SSPP_DMA: break; case SSPP_VIG: break; case DSPP: if (pp_sts_is_enabled(pp_sts->igc_sts, side)) *opmode |= IGC_DSPP_OP_MODE_EN; break; case LM: if (pp_sts->argc_sts & PP_STS_ENABLE) pr_debug("pgc in LM enabled\n"); break; default: pr_err("Invalid block type %d\n", location); break; } } static void pp_pa_set_global_adj_regs(char __iomem *base_addr, struct mdp_pa_data_v1_7 *pa_data, u32 flags) { char __iomem *addr = NULL; addr = base_addr + PA_HSIC_REG_OFF; if (flags & MDP_PP_PA_HUE_ENABLE) writel_relaxed((pa_data->global_hue_adj & REG_MASK(12)), addr); addr += 4; if (flags & MDP_PP_PA_SAT_ENABLE) writel_relaxed((pa_data->global_sat_adj & REG_MASK(16)), addr); addr += 4; if (flags & MDP_PP_PA_VAL_ENABLE) writel_relaxed((pa_data->global_val_adj & REG_MASK(8)), addr); addr += 4; if (flags & MDP_PP_PA_CONT_ENABLE) writel_relaxed((pa_data->global_cont_adj & REG_MASK(8)), addr); } static void pp_pa_set_mem_col(char __iomem *base_addr, struct mdp_pa_data_v1_7 *pa_data, u32 flags) { char __iomem *mem_col_base = NULL, *mem_col_p2 = NULL; struct mdp_pa_mem_col_data_v1_7 *mem_col_data = NULL; uint32_t mask = 0, hold = 0, hold_mask = 0; uint32_t hold_curr = 0; flags &= (MDP_PP_PA_SKIN_ENABLE | MDP_PP_PA_SKY_ENABLE | MDP_PP_PA_FOL_ENABLE); if (!flags) return; while (flags) { if (flags & MDP_PP_PA_SKIN_ENABLE) { flags &= ~MDP_PP_PA_SKIN_ENABLE; mem_col_base = base_addr + PA_MEM_SKIN_REG_OFF; mem_col_p2 = base_addr + PA_MEM_SKIN_ADJUST_P2_REG_OFF; mem_col_data = &pa_data->skin_cfg; hold |= pa_data->skin_cfg.sat_hold & REG_MASK(2); hold |= (pa_data->skin_cfg.val_hold & REG_MASK(2)) << 2; hold_mask |= REG_MASK(4); } else if (flags & MDP_PP_PA_SKY_ENABLE) { flags &= ~MDP_PP_PA_SKY_ENABLE; mem_col_base = base_addr + PA_MEM_SKY_REG_OFF; mem_col_p2 = base_addr + PA_MEM_SKY_ADJUST_P2_REG_OFF; mem_col_data = &pa_data->sky_cfg; hold |= (pa_data->sky_cfg.sat_hold & REG_MASK(2)) << 4; hold |= (pa_data->sky_cfg.val_hold & REG_MASK(2)) << 6; hold_mask |= REG_MASK_SHIFT(4, 4); } else if (flags & MDP_PP_PA_FOL_ENABLE) { flags &= ~MDP_PP_PA_FOL_ENABLE; mem_col_base = base_addr + PA_MEM_FOL_REG_OFF; mem_col_p2 = base_addr + PA_MEM_FOL_ADJUST_P2_REG_OFF; mem_col_data = &pa_data->fol_cfg; hold |= (pa_data->fol_cfg.sat_hold & REG_MASK(2)) << 8; hold |= (pa_data->fol_cfg.val_hold & REG_MASK(2)) << 10; hold_mask |= REG_MASK_SHIFT(4, 8); } else { break; } mask = REG_MASK_SHIFT(16, 16) | REG_MASK(11); writel_relaxed((mem_col_data->color_adjust_p0 & mask), mem_col_base); mem_col_base += 4; mask = U32_MAX; writel_relaxed((mem_col_data->color_adjust_p1 & mask), mem_col_base); mem_col_base += 4; mask = REG_MASK_SHIFT(11, 16) | REG_MASK(11); writel_relaxed((mem_col_data->hue_region & mask), mem_col_base); mem_col_base += 4; mask = REG_MASK(24); writel_relaxed((mem_col_data->sat_region & mask), mem_col_base); mem_col_base += 4; /* mask is same for val and sat */ writel_relaxed((mem_col_data->val_region & mask), mem_col_base); mask = U32_MAX; writel_relaxed((mem_col_data->color_adjust_p2 & mask), mem_col_p2); mem_col_p2 += 4; writel_relaxed((mem_col_data->blend_gain & mask), mem_col_p2); } hold_curr = readl_relaxed(base_addr + PA_PWL_HOLD_REG_OFF) & REG_MASK(16); hold_curr &= ~hold_mask; hold = hold_curr | (hold & hold_mask); writel_relaxed(hold, (base_addr + PA_PWL_HOLD_REG_OFF)); } static void pp_pa_set_six_zone(char __iomem *base_addr, struct mdp_pa_data_v1_7 *pa_data, u32 flags) { char __iomem *addr = base_addr + PA_SZONE_REG_OFF; uint32_t mask_p0 = 0, mask_p1 = 0, hold = 0, hold_mask = 0; uint32_t hold_curr = 0; int i = 0; if (!(flags & MDP_PP_PA_SIX_ZONE_ENABLE)) return; if (pa_data->six_zone_len != MDP_SIX_ZONE_LUT_SIZE || !pa_data->six_zone_curve_p0 || !pa_data->six_zone_curve_p1) { pr_err("Invalid six zone data: len %d curve_p0 %pK curve_p1 %pK\n", pa_data->six_zone_len, pa_data->six_zone_curve_p0, pa_data->six_zone_curve_p1); return; } mask_p0 = REG_MASK(12); mask_p1 = REG_MASK(12) | REG_MASK_SHIFT(12, 16); writel_relaxed((pa_data->six_zone_curve_p1[0] & mask_p1), addr + 4); /* Update the index to 0 and write value */ writel_relaxed((pa_data->six_zone_curve_p0[0] & mask_p0) | BIT(26), addr); for (i = 1; i < MDP_SIX_ZONE_LUT_SIZE; i++) { writel_relaxed((pa_data->six_zone_curve_p1[i] & mask_p1), addr + 4); writel_relaxed((pa_data->six_zone_curve_p0[i] & mask_p0), addr); } addr += 8; writel_relaxed(pa_data->six_zone_thresh, addr); addr += 4; writel_relaxed(pa_data->six_zone_adj_p0 & REG_MASK(16), addr); addr += 4; writel_relaxed(pa_data->six_zone_adj_p1, addr); hold = (pa_data->six_zone_sat_hold & REG_MASK(2)) << 12; hold |= (pa_data->six_zone_val_hold & REG_MASK(2)) << 14; hold_mask = REG_MASK_SHIFT(4, 12); hold_curr = readl_relaxed(base_addr + PA_PWL_HOLD_REG_OFF) & REG_MASK(16); hold_curr &= ~hold_mask; hold = hold_curr | (hold & hold_mask); writel_relaxed(hold, (base_addr + PA_PWL_HOLD_REG_OFF)); } static void pp_pa_opmode_config(char __iomem *base_addr, struct pp_sts_type *pp_sts) { uint32_t opmode = 0; /* set the PA bits */ if (pp_sts->pa_sts & PP_STS_ENABLE) { opmode |= BIT(20); if (pp_sts->pa_sts & PP_STS_PA_HUE_MASK) opmode |= BIT(25); if (pp_sts->pa_sts & PP_STS_PA_SAT_MASK) opmode |= BIT(26); if (pp_sts->pa_sts & PP_STS_PA_VAL_MASK) opmode |= BIT(27); if (pp_sts->pa_sts & PP_STS_PA_CONT_MASK) opmode |= BIT(28); if (pp_sts->pa_sts & PP_STS_PA_SAT_ZERO_EXP_EN) opmode |= BIT(1); if (pp_sts->pa_sts & PP_STS_PA_MEM_COL_SKIN_MASK) opmode |= BIT(5); if (pp_sts->pa_sts & PP_STS_PA_MEM_COL_FOL_MASK) opmode |= BIT(6); if (pp_sts->pa_sts & PP_STS_PA_MEM_COL_SKY_MASK) opmode |= BIT(7); if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_HUE_MASK) opmode |= BIT(29); if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_SAT_MASK) opmode |= BIT(30); if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_VAL_MASK) opmode |= BIT(31); if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_HUE_EN) opmode |= BIT(22); if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_SAT_EN) opmode |= BIT(23); if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_VAL_EN) opmode |= BIT(24); if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_CONT_EN) opmode |= BIT(18); if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_BLEND_EN) opmode |= BIT(3); if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_SIX_EN) opmode |= BIT(17); } /* reset hist_en, hist_lutv_en and hist_lutv_first_en bits based on the pp_sts */ if (pp_sts->hist_sts & PP_STS_ENABLE) opmode |= BIT(16); if (pp_sts->enhist_sts & PP_STS_ENABLE) opmode |= BIT(19) | BIT(20); if (pp_sts->enhist_sts & PP_STS_PA_LUT_FIRST) opmode |= BIT(21); writel_relaxed(opmode, base_addr + PA_OP_MODE_REG_OFF); } static int pp_pa_dither_get_version(u32 *version) { if (!version) { pr_err("invalid param version"); return -EINVAL; } *version = mdp_dither_pa_v1_7; return 0; } static int pp_pa_dither_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { struct mdp_dither_cfg_data *dither_cfg_data = NULL; struct mdp_pa_dither_res_data_v1_7 *dither_data = NULL; u32 *pdata; u32 opmode = 0, data = 0, i = 0; char __iomem *opmode_addr = NULL, *matrix_addr = NULL; if (!base_addr || !cfg_data || !pp_sts) { pr_err("invalid params base_addr %pK cfg_data %pK pp_sts_type %pK\n", base_addr, cfg_data, pp_sts); return -EINVAL; } if (block_type != DSPP) { pr_err("Invalid block type %d\n", block_type); return -EINVAL; } dither_cfg_data = (struct mdp_dither_cfg_data *) cfg_data; if (dither_cfg_data->version != mdp_dither_pa_v1_7) { pr_err("invalid pa dither version %d\n", dither_cfg_data->version); return -EINVAL; } if (!(dither_cfg_data->flags & ~(MDP_PP_OPS_READ))) { pr_debug("only read ops is set %d", dither_cfg_data->flags); return 0; } opmode_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_DITHER_REG_OFF; if (dither_cfg_data->flags & MDP_PP_OPS_DISABLE || !(dither_cfg_data->flags & MDP_PP_OPS_WRITE)) { pr_debug("Disable pa dither/No write ops set flags %x", dither_cfg_data->flags); goto dither_set_sts; } matrix_addr = opmode_addr + 4; dither_data = (struct mdp_pa_dither_res_data_v1_7 *) dither_cfg_data->cfg_payload; if (!dither_data) { pr_err("invalid payload for dither\n"); return -EINVAL; } pdata = dither_data->matrix_data; for (i = 0; i < MDP_DITHER_DATA_V1_7_SZ; i += 4) { data = (pdata[i] & REG_MASK(4)) | ((pdata[i + 1] & REG_MASK(4)) << 4) | ((pdata[i + 2] & REG_MASK(4)) << 8) | ((pdata[i + 3] & REG_MASK(4)) << 12); writel_relaxed(data, matrix_addr); matrix_addr += 4; } opmode = BIT(0); opmode |= (dither_data->offset_en) ? BIT(1) : 0; opmode |= ((dither_data->strength) & REG_MASK(4)) << 4; dither_set_sts: if (dither_cfg_data->flags & MDP_PP_OPS_DISABLE) { pp_sts->pa_dither_sts &= ~PP_STS_ENABLE; writel_relaxed(0, opmode_addr); } else if (dither_cfg_data->flags & MDP_PP_OPS_ENABLE) { pp_sts->pa_dither_sts |= PP_STS_ENABLE; writel_relaxed(opmode, opmode_addr); } return 0; } static int pp_igc_dither_set_strength(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { struct mdp_igc_lut_data *lut_cfg_data = cfg_data; struct mdp_igc_lut_data_config *v3_data = NULL; if (!base_addr || !cfg_data || (block_type != DSPP) || !pp_sts || (lut_cfg_data->version != mdp_igc_v3)) { pr_err("invalid params base_addr %pK cfg_data %pK block_type %d igc version %d\n", base_addr, cfg_data, block_type, (lut_cfg_data ? lut_cfg_data->version : mdp_pp_unknown)); return -EINVAL; } if ((lut_cfg_data->ops & MDP_PP_OPS_DISABLE) || !(lut_cfg_data->ops & MDP_PP_OPS_WRITE)) return 0; if (!lut_cfg_data->cfg_payload) { pr_err("invalid payload igc dither strenth\n"); return -EINVAL; } v3_data = lut_cfg_data->cfg_payload; writel_relaxed(v3_data->strength, base_addr + IGC_DITHER_STRENGTH_REG_OFF); return 0; } static int pp_igc_set_config(char __iomem *base_addr, struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type) { struct mdp_igc_lut_data *lut_cfg_data = NULL, v17_cfg_data = {0}; struct mdp_igc_lut_data_config *v3_data = NULL; struct mdp_igc_lut_data_v1_7 v17_lut_data = {0}; int ret = 0; if (!base_addr || !pp_sts || !cfg_data || !config_data.igc_set_config) { pr_err("invalid payload base_addr %pK pp_sts %pK cfg_data %pK igc_set_config %pK\n", base_addr, pp_sts, cfg_data, config_data.igc_set_config); return -EINVAL; } lut_cfg_data = cfg_data; v3_data = lut_cfg_data->cfg_payload; if (v3_data) { v17_lut_data.c0_c1_data = v3_data->c0_c1_data; v17_lut_data.c2_data = v3_data->c2_data; v17_lut_data.len = v3_data->len; v17_lut_data.table_fmt = v3_data->table_fmt; } memcpy(&v17_cfg_data, lut_cfg_data, sizeof(v17_cfg_data)); v17_cfg_data.version = mdp_igc_v1_7; ret = config_data.igc_set_config(base_addr, pp_sts, &v17_cfg_data, block_type); return ret; } static int pp_igc_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num) { return -EINVAL; } static int pp_igc_get_version(u32 *version) { if (!version) { pr_err("invalid param version"); return -EINVAL; } *version = mdp_igc_v3; return 0; }