diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 4e97c3849bcf..2b0ddce10cba 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -737,27 +737,25 @@ static void pp_igc_config(unsigned long flags, char __iomem *addr, u32 pipe_num, u32 pipe_cnt) { u32 tbl_idx; - if (flags & PP_FLAGS_DIRTY_IGC) { - if (igc_config->ops & MDP_PP_OPS_WRITE) - pp_update_igc_lut(igc_config, addr, pipe_num, - pipe_cnt); + if (igc_config->ops & MDP_PP_OPS_WRITE) + pp_update_igc_lut(igc_config, addr, pipe_num, + pipe_cnt); - if (igc_config->ops & MDP_PP_IGC_FLAG_ROM0) { - pp_sts->pcc_sts |= PP_STS_ENABLE; - tbl_idx = 1; - } else if (igc_config->ops & MDP_PP_IGC_FLAG_ROM1) { - pp_sts->pcc_sts |= PP_STS_ENABLE; - tbl_idx = 2; - } else { - tbl_idx = 0; - } - pp_sts->igc_tbl_idx = tbl_idx; - if (igc_config->ops & MDP_PP_OPS_DISABLE) - pp_sts->igc_sts &= ~PP_STS_ENABLE; - else if (igc_config->ops & MDP_PP_OPS_ENABLE) - pp_sts->igc_sts |= PP_STS_ENABLE; - pp_sts_set_split_bits(&pp_sts->igc_sts, igc_config->ops); + if (igc_config->ops & MDP_PP_IGC_FLAG_ROM0) { + pp_sts->igc_sts |= PP_STS_ENABLE; + tbl_idx = 1; + } else if (igc_config->ops & MDP_PP_IGC_FLAG_ROM1) { + pp_sts->igc_sts |= PP_STS_ENABLE; + tbl_idx = 2; + } else { + tbl_idx = 0; } + pp_sts->igc_tbl_idx = tbl_idx; + if (igc_config->ops & MDP_PP_OPS_DISABLE) + pp_sts->igc_sts &= ~PP_STS_ENABLE; + else if (igc_config->ops & MDP_PP_OPS_ENABLE) + pp_sts->igc_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->igc_sts, igc_config->ops); } static void pp_enhist_config(unsigned long flags, char __iomem *addr, @@ -1569,7 +1567,7 @@ static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num, return; if (pp_driver_ops.pp_opmode_config) { - pp_driver_ops.pp_opmode_config(PP_OPMODE_DSPP, + pp_driver_ops.pp_opmode_config(DSPP, pp_sts, opmode, side); return; } @@ -1709,9 +1707,21 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) } } - pp_igc_config(flags, mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE, - pp_sts, &mdss_pp_res->igc_disp_cfg[disp_num], - dspp_num, mdata->ndspp); + if (flags & PP_FLAGS_DIRTY_IGC) { + if (!pp_ops[IGC].pp_set_config) { + pp_igc_config(flags, + mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE, + pp_sts, &mdss_pp_res->igc_disp_cfg[disp_num], + dspp_num, mdata->ndspp); + } else { + addr = mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE; + /* Pass dspp num using block */ + mdss_pp_res->igc_disp_cfg[disp_num].block = dspp_num; + pp_ops[IGC].pp_set_config(addr, pp_sts, + &mdss_pp_res->igc_disp_cfg[disp_num], + DSPP); + } + } pp_enhist_config(flags, base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE, pp_sts, &mdss_pp_res->enhist_disp_cfg[disp_num]); @@ -2926,9 +2936,16 @@ int mdss_mdp_igc_lut_config(struct mdp_igc_lut_data *config, &mdss_pp_res->igc_lut_c2[disp_num][0]; if (mdata->has_no_lut_read) pp_read_igc_lut_cached(&local_cfg); - else - pp_read_igc_lut(&local_cfg, igc_addr, dspp_num, - mdata->ndspp); + else { + if (pp_ops[IGC].pp_get_config) { + pp_ops[IGC].pp_get_config(igc_addr, config, + DSPP, disp_num); + goto clock_off; + } else { + pp_read_igc_lut(&local_cfg, igc_addr, + dspp_num, mdata->ndspp); + } + } if (copy_to_user(config->c0_c1_data, local_cfg.c0_c1_data, config->len * sizeof(u32))) { ret = -EFAULT; @@ -2942,8 +2959,18 @@ int mdss_mdp_igc_lut_config(struct mdp_igc_lut_data *config, goto igc_config_exit; } *copyback = 1; +clock_off: mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); } else { + if (pp_ops[IGC].pp_set_config) { + ret = pp_igc_lut_cache_params(config, + mdss_pp_res, copy_from_kernel); + if (ret) { + pr_err("igc caching failed ret %d", ret); + goto igc_config_exit; + } else + goto igc_set_dirty; + } if (copy_from_kernel) { memcpy(&mdss_pp_res->igc_lut_c0c1[disp_num][0], config->c0_c1_data, config->len * sizeof(u32)); @@ -2969,6 +2996,7 @@ int mdss_mdp_igc_lut_config(struct mdp_igc_lut_data *config, &mdss_pp_res->igc_lut_c0c1[disp_num][0]; mdss_pp_res->igc_disp_cfg[disp_num].c2_data = &mdss_pp_res->igc_lut_c2[disp_num][0]; +igc_set_dirty: mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_IGC; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.h b/drivers/video/fbdev/msm/mdss_mdp_pp.h index 04f008fe1207..0f0b7575e11e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.h +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.h @@ -89,6 +89,9 @@ struct mdp_pp_driver_ops { }; struct mdss_pp_res_type_v1_7 { + u32 igc_table_c0_c1[MDSS_BLOCK_DISP_NUM][IGC_LUT_ENTRIES]; + u32 igc_table_c2[MDSS_BLOCK_DISP_NUM][IGC_LUT_ENTRIES]; + struct mdp_igc_lut_data_v1_7 igc_v17_data[MDSS_BLOCK_DISP_NUM]; struct mdp_gamut_data_v1_7 gamut_v17_data[MDSS_BLOCK_DISP_NUM]; struct mdp_pcc_data_v1_7 pcc_v17_data[MDSS_BLOCK_DISP_NUM]; }; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c index 292ed8405b22..5bc73a63c2af 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c @@ -285,3 +285,112 @@ int pp_pcc_cache_params(struct mdp_pcc_cfg_data *config, } return ret; } + +int pp_igc_lut_cache_params_v1_7(struct mdp_igc_lut_data *config, + struct mdss_pp_res_type *mdss_pp_res, + u32 copy_from_kernel) +{ + int ret = 0; + struct mdss_pp_res_type_v1_7 *res_cache; + struct mdp_igc_lut_data_v1_7 *v17_cache_data, v17_usr_config; + u32 disp_num; + if (!config || !mdss_pp_res) { + pr_err("invalid param config %p pp_res %p\n", + config, mdss_pp_res); + return -EINVAL; + } + if ((config->block < MDP_LOGICAL_BLOCK_DISP_0) || + (config->block >= MDP_BLOCK_MAX)) { + pr_err("invalid config block %d\n", config->block); + return -EINVAL; + } + if (!mdss_pp_res->pp_data_res) { + pr_err("invalid pp_data_res %p\n", mdss_pp_res->pp_data_res); + return -EINVAL; + } + res_cache = mdss_pp_res->pp_data_res; + if (config->ops & MDP_PP_OPS_READ) { + pr_err("read op is not supported\n"); + return -EINVAL; + } else { + disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0; + mdss_pp_res->igc_disp_cfg[disp_num] = *config; + v17_cache_data = &res_cache->igc_v17_data[disp_num]; + mdss_pp_res->igc_disp_cfg[disp_num].cfg_payload = + (void *) v17_cache_data; + if (copy_from_user(&v17_usr_config, config->cfg_payload, + sizeof(v17_usr_config))) { + pr_err("failed to copy igc config\n"); + ret = -EFAULT; + goto igc_config_exit; + } + if (!(config->ops & MDP_PP_OPS_WRITE)) { + pr_debug("op for gamut %d\n", config->ops); + goto igc_config_exit; + } + if (v17_usr_config.len != IGC_LUT_ENTRIES) { + pr_err("Invalid table size %d exp %d\n", + v17_usr_config.len, IGC_LUT_ENTRIES); + ret = -EINVAL; + goto igc_config_exit; + } + memcpy(v17_cache_data, &v17_usr_config, + sizeof(v17_usr_config)); + v17_cache_data->c0_c1_data = + &res_cache->igc_table_c0_c1[disp_num][0]; + v17_cache_data->c2_data = + &res_cache->igc_table_c2[disp_num][0]; + if (copy_from_kernel) { + memcpy(v17_cache_data->c0_c1_data, + v17_usr_config.c0_c1_data, + v17_usr_config.len * sizeof(u32)); + memcpy(v17_cache_data->c2_data, v17_usr_config.c2_data, + v17_usr_config.len * sizeof(u32)); + } else { + ret = copy_from_user(v17_cache_data->c0_c1_data, + v17_usr_config.c0_c1_data, + v17_usr_config.len * sizeof(u32)); + if (ret) { + pr_err("copy from user failed for c0_c1_data size %zd ret %d\n", + v17_usr_config.len * sizeof(u32), ret); + ret = -EFAULT; + goto igc_config_exit; + } + ret = copy_from_user(v17_cache_data->c2_data, + v17_usr_config.c2_data, + v17_usr_config.len * sizeof(u32)); + if (ret) { + pr_err("copy from user failed for c2_data size %zd ret %d\n", + v17_usr_config.len * sizeof(u32), ret); + ret = -EFAULT; + goto igc_config_exit; + } + } + } +igc_config_exit: + return ret; +} + +int pp_igc_lut_cache_params(struct mdp_igc_lut_data *config, + struct mdss_pp_res_type *mdss_pp_res, + u32 copy_from_kernel) +{ + int ret = 0; + if (!config || !mdss_pp_res) { + pr_err("invalid param config %p pp_res %p\n", + config, mdss_pp_res); + return -EINVAL; + } + switch (config->version) { + case mdp_igc_v1_7: + ret = pp_igc_lut_cache_params_v1_7(config, mdss_pp_res, + copy_from_kernel); + break; + default: + pr_err("unsupported igc version %d\n", + config->version); + ret = -EINVAL; + break; + } + return ret; +} diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h index 86d00a727515..2b7c701627a4 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h @@ -21,4 +21,8 @@ int pp_gamut_cache_params(struct mdp_gamut_cfg_data *config, int pp_pcc_cache_params(struct mdp_pcc_cfg_data *config, struct mdss_pp_res_type *mdss_pp_res); +int pp_igc_lut_cache_params(struct mdp_igc_lut_data *config, + struct mdss_pp_res_type *mdss_pp_res, + u32 copy_from_kernel); + #endif diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c index 0df1ac1bb231..2cb90eb328e5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c @@ -36,6 +36,7 @@ #define PCC_CONST_COEFF_MASK 0xFFFF #define PCC_COEFF_MASK 0x3FFFF + #define GAMUT_OP_MODE_OFF 0 #define GAMUT_TABLE_INDEX 4 #define GAMUT_TABLE_UPPER_R 8 @@ -50,6 +51,17 @@ #define GAMUT_FINE_INDEX 0 #define GAMUT_ENABLE BIT(0) +#define IGC_MASK_MAX 3 +#define IGC_C0_LUT 0 +#define IGC_RGB_C0_LUT 0xC +#define IGC_DMA_C0_LUT 0x18 +#define IGC_CONFIG_MASK(n) \ + ((((1 << (IGC_MASK_MAX + 1)) - 1) & ~(1 << n)) << 28) +#define IGC_INDEX_UPDATE BIT(25) +#define IGC_INDEX_VALUE_UPDATE (BIT(24) | IGC_INDEX_UPDATE) +#define IGC_DATA_MASK (BIT(12) - 1) +#define IGC_DSPP_OP_MODE_EN BIT(0) + static struct mdss_pp_res_type_v1_7 config_data; static void pp_opmode_config(int location, struct pp_sts_type *pp_sts, @@ -68,6 +80,12 @@ static int pp_pcc_set_config(char __iomem *base_addr, static int pp_pcc_get_config(char __iomem *base_addr, void *cfg_data, u32 block_type, u32 disp_num); +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); + void *pp_get_driver_ops(struct mdp_pp_driver_ops *ops) { if (!ops) { @@ -76,8 +94,8 @@ void *pp_get_driver_ops(struct mdp_pp_driver_ops *ops) } /* IGC ops */ - ops->pp_ops[IGC].pp_set_config = NULL; - ops->pp_ops[IGC].pp_get_config = NULL; + ops->pp_ops[IGC].pp_set_config = pp_igc_set_config; + ops->pp_ops[IGC].pp_get_config = pp_igc_get_config; /* PCC ops */ ops->pp_ops[PCC].pp_set_config = pp_pcc_set_config; @@ -124,7 +142,23 @@ static void pp_opmode_config(int location, struct pp_sts_type *pp_sts, pr_err("Invalid pp_sts %p or opmode %p\n", pp_sts, opmode); return; } - + switch (location) { + case SSPP_RGB: + break; + 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: + break; + default: + pr_err("Invalid block type %d\n", location); + break; + } return; } @@ -517,3 +551,174 @@ static int pp_pcc_get_config(char __iomem *base_addr, void *cfg_data, return 0; } + +static int pp_igc_set_config(char __iomem *base_addr, + struct pp_sts_type *pp_sts, void *cfg_data, + u32 block_type) +{ + int ret = 0, i = 0; + struct mdp_igc_lut_data *lut_cfg_data = NULL; + struct mdp_igc_lut_data_v1_7 *lut_data = NULL; + char __iomem *c0 = NULL, *c1 = NULL, *c2 = NULL; + u32 data; + + if (!base_addr || !cfg_data || !pp_sts) { + pr_err("invalid params base_addr %p cfg_data %p pp_sts_type %p\n", + base_addr, cfg_data, pp_sts); + return -EINVAL; + } + + lut_cfg_data = (struct mdp_igc_lut_data *) cfg_data; + if (lut_cfg_data->version != mdp_igc_v1_7 || + !lut_cfg_data->cfg_payload) { + pr_err("invalid igc version %d payload %p\n", + lut_cfg_data->version, lut_cfg_data->cfg_payload); + 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->block > IGC_MASK_MAX) { + pr_err("invalid mask value for IGC %d", lut_cfg_data->block); + return -EINVAL; + } + if (!(lut_cfg_data->ops & MDP_PP_OPS_WRITE)) { + pr_debug("non write ops set %d\n", lut_cfg_data->ops); + goto bail_out; + } + lut_data = lut_cfg_data->cfg_payload; + if (lut_data->len != IGC_LUT_ENTRIES || !lut_data->c0_c1_data || + !lut_data->c2_data) { + pr_err("invalid lut len %d c0_c1_data %p c2_data %p\n", + lut_data->len, lut_data->c0_c1_data, lut_data->c2_data); + return -EINVAL; + } + switch (block_type) { + case SSPP_RGB: + c0 = base_addr + IGC_RGB_C0_LUT; + break; + case SSPP_DMA: + c0 = base_addr + IGC_DMA_C0_LUT; + break; + case SSPP_VIG: + case DSPP: + c0 = base_addr + IGC_C0_LUT; + break; + default: + pr_err("Invalid block type %d\n", block_type); + ret = -EINVAL; + break; + } + if (ret) { + pr_err("igc table not updated ret %d\n", ret); + return ret; + } + c1 = c0 + 4; + c2 = c1 + 4; + data = IGC_INDEX_UPDATE | IGC_CONFIG_MASK(lut_cfg_data->block); + pr_debug("data %x block type %d mask %x\n", + data, lut_cfg_data->block, + IGC_CONFIG_MASK(lut_cfg_data->block)); + writel_relaxed((lut_data->c0_c1_data[0] & IGC_DATA_MASK) | data, c0); + writel_relaxed(((lut_data->c0_c1_data[0] >> 16) + & IGC_DATA_MASK) | data, c1); + writel_relaxed((lut_data->c2_data[0] & IGC_DATA_MASK) | data, c2); + data &= ~IGC_INDEX_UPDATE; + /* update the index for c0, c1 , c2 */ + for (i = 1; i < IGC_LUT_ENTRIES; i++) { + writel_relaxed((lut_data->c0_c1_data[i] & IGC_DATA_MASK) + | data, c0); + writel_relaxed(((lut_data->c0_c1_data[i] >> 16) + & IGC_DATA_MASK) | data, c1); + writel_relaxed((lut_data->c2_data[i] & IGC_DATA_MASK) + | data, c2); + } +bail_out: + if (!ret) { + if (lut_cfg_data->ops & MDP_PP_OPS_DISABLE) + pp_sts->igc_sts &= ~PP_STS_ENABLE; + else if (lut_cfg_data->ops & MDP_PP_OPS_ENABLE) + pp_sts->igc_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->igc_sts, + lut_cfg_data->ops); + } + return ret; +} + +static int pp_igc_get_config(char __iomem *base_addr, void *cfg_data, + u32 block_type, u32 disp_num) +{ + int ret = 0, i = 0; + struct mdp_igc_lut_data *lut_cfg_data = NULL; + struct mdp_igc_lut_data_v1_7 *lut_data = NULL; + char __iomem *c1 = NULL, *c2 = NULL; + u32 *c0c1_data = NULL, *c2_data = NULL; + u32 data = 0, sz = 0; + + if (!base_addr || !cfg_data || block_type != DSPP) { + pr_err("invalid params base_addr %p cfg_data %p block_type %d\n", + base_addr, cfg_data, block_type); + return -EINVAL; + } + lut_cfg_data = (struct mdp_igc_lut_data *) cfg_data; + if (!(lut_cfg_data->ops & MDP_PP_OPS_READ)) { + pr_err("read ops not set for lut ops %d\n", lut_cfg_data->ops); + return ret; + } + if (lut_cfg_data->version != mdp_igc_v1_7 || + !lut_cfg_data->cfg_payload || + lut_cfg_data->block > IGC_MASK_MAX) { + pr_err("invalid igc version %d payload %p block %d\n", + lut_cfg_data->version, lut_cfg_data->cfg_payload, + lut_cfg_data->block); + ret = -EINVAL; + goto exit; + } + lut_data = lut_cfg_data->cfg_payload; + if (lut_data->len != IGC_LUT_ENTRIES) { + pr_err("invalid lut len %d\n", lut_data->len); + ret = -EINVAL; + goto exit; + } + sz = IGC_LUT_ENTRIES * sizeof(u32); + if (!access_ok(VERIFY_WRITE, lut_data->c0_c1_data, sz) || + (!access_ok(VERIFY_WRITE, lut_data->c2_data, sz))) { + pr_err("invalid lut address for sz %d\n", sz); + ret = -EFAULT; + goto exit; + } + /* Allocate for c0c1 and c2 tables */ + c0c1_data = kzalloc(sz * 2, GFP_KERNEL); + if (!c0c1_data) { + pr_err("allocation failed for c0c1 size %d\n", sz * 2); + ret = -ENOMEM; + goto exit; + } + c2_data = &c0c1_data[IGC_LUT_ENTRIES]; + data = IGC_INDEX_VALUE_UPDATE | IGC_CONFIG_MASK(lut_cfg_data->block); + pr_debug("data %x block type %d mask %x\n", + data, lut_cfg_data->block, + IGC_CONFIG_MASK(lut_cfg_data->block)); + c1 = base_addr + 4; + c2 = c1 + 4; + writel_relaxed(data, base_addr); + writel_relaxed(data, c1); + writel_relaxed(data, c2); + for (i = 0; i < IGC_LUT_ENTRIES; i++) { + c0c1_data[i] = readl_relaxed(base_addr) & IGC_DATA_MASK; + c0c1_data[i] |= (readl_relaxed(c1) & IGC_DATA_MASK) << 16; + c2_data[i] = readl_relaxed(c2) & IGC_DATA_MASK; + } + if (copy_to_user(lut_data->c0_c1_data, c0c1_data, sz)) { + pr_err("failed to copy the c0c1 data"); + ret = -EFAULT; + } + if (!ret && copy_to_user(lut_data->c2_data, c2_data, sz)) { + pr_err("failed to copy the c2 data"); + ret = -EFAULT; + } + kfree(c0c1_data); +exit: + return ret; +} diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index beb464c6e21a..8faaa3bc3a98 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -526,6 +526,18 @@ struct mdp_pa_v2_cfg_data { void *cfg_payload; }; +enum { + mdp_igc_v1_7 = 1, + mdp_igc_vmax, +}; + +enum { + mdp_igc_rec601, + mdp_igc_rec709, + mdp_igc_custom, + mdp_igc_mode_max, +}; + struct mdp_igc_lut_data { uint32_t block; uint32_t version; @@ -536,6 +548,7 @@ struct mdp_igc_lut_data { }; struct mdp_igc_lut_data_v1_7 { + uint32_t table_fmt; uint32_t len; uint32_t *c0_c1_data; uint32_t *c2_data;