summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorGopikrishnaiah Anandan <agopik@codeaurora.org>2016-02-25 11:25:11 -0800
committerKyle Yan <kyan@codeaurora.org>2016-06-07 11:55:10 -0700
commit68f301791adcd442e10255eafeaeb039d0762170 (patch)
tree1a68fa693b050fbaaae77e7dc5b675ab9b35cbcb /drivers/video/fbdev
parent9b03c7ad99bb4e61ad3f684bfe9bef6bdf6d0dc6 (diff)
msm: mdss: Add PA dither support for msmcobalt
Picture adjustment block on msmcobalt mdss supports dithering module. Module can be programmed by driver clients for a logical display. Change adds support for enabling the PA dither block. Change-Id: I8ae05d0f98a33a8608a4caef93d50e4dabad05a1 Signed-off-by: Gopikrishnaiah Anandan <agopik@codeaurora.org> Signed-off-by: Ping Li <pingli@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c65
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.h11
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c103
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_v3.c114
7 files changed, 289 insertions, 17 deletions
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index e67a7a60fddb..e7bc1c607d70 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -687,6 +687,7 @@ struct pp_sts_type {
u32 sharp_sts;
u32 hist_sts;
u32 side_sts;
+ u32 pa_dither_sts;
};
struct mdss_pipe_pp_res {
@@ -1709,6 +1710,9 @@ int mdss_mdp_dither_config(struct msm_fb_data_type *mfd,
int copy_from_kernel);
int mdss_mdp_gamut_config(struct msm_fb_data_type *mfd,
struct mdp_gamut_cfg_data *config, u32 *copyback);
+int mdss_mdp_pa_dither_config(struct msm_fb_data_type *mfd,
+ struct mdp_dither_cfg_data *config);
+
int mdss_mdp_hist_intr_req(struct mdss_intr *intr, u32 bits, bool en);
int mdss_mdp_hist_intr_setup(struct mdss_intr *intr, int state);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index ab02c2a12efd..0173f73ffe6b 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -4089,6 +4089,10 @@ static int mdss_mdp_pp_ioctl(struct msm_fb_data_type *mfd,
case mdp_op_calib_dcm_state:
ret = mdss_fb_dcm(mfd, mdp_pp.data.calib_dcm.dcm_state);
break;
+ case mdp_op_pa_dither_cfg:
+ ret = mdss_mdp_pa_dither_config(mfd,
+ &mdp_pp.data.dither_cfg_data);
+ break;
default:
pr_err("Unsupported request to MDP_PP IOCTL. %d = op\n",
mdp_pp.op);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index f92d4bb9ed1d..d65985a13cf1 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -355,6 +355,8 @@ static u32 igc_limited[IGC_LUT_ENTRIES] = {
#define PP_FLAGS_DIRTY_HIST_COL 0x80
#define PP_FLAGS_DIRTY_PGC 0x100
#define PP_FLAGS_DIRTY_SHARP 0x200
+#define PP_FLAGS_DIRTY_PA_DITHER 0x400
+
/* Leave space for future features */
#define PP_FLAGS_RESUME_COMMIT 0x10000000
@@ -2391,6 +2393,12 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
pgc_config->flags);
}
}
+ if (flags & PP_FLAGS_DIRTY_PA_DITHER &&
+ pp_ops[PA_DITHER].pp_set_config) {
+ pp_ops[PA_DITHER].pp_set_config(base, pp_sts,
+ &mdss_pp_res->pa_dither_cfg[disp_num],
+ DSPP);
+ }
pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev, &opmode);
@@ -2716,7 +2724,13 @@ int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd)
mdss_pp_res->pgc_disp_cfg[disp_num].flags |=
MDP_PP_OPS_WRITE;
}
-
+ if (pp_sts.pa_dither_sts & PP_STS_ENABLE) {
+ flags |= PP_FLAGS_DIRTY_PA_DITHER;
+ if (!(mdss_pp_res->pa_dither_cfg[disp_num].flags
+ & MDP_PP_OPS_DISABLE))
+ mdss_pp_res->pa_dither_cfg[disp_num].flags |=
+ MDP_PP_OPS_WRITE;
+ }
mdss_pp_res->pp_disp_flags[disp_num] |= flags;
mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_RESUME_COMMIT;
@@ -7138,7 +7152,7 @@ int mdss_mdp_pp_get_version(struct mdp_pp_feature_version *version)
ret = -EINVAL;
goto exit_version;
}
- if (version->pp_feature >= PP_FEATURE_MAX) {
+ if (version->pp_feature >= PP_MAX_FEATURES) {
pr_err("invalid feature passed %d\n", version->pp_feature);
ret = -EINVAL;
goto exit_version;
@@ -7509,3 +7523,50 @@ static int pp_ppb_setup(struct mdss_mdp_mixer *mixer)
}
return ret;
}
+
+int mdss_mdp_pa_dither_config(struct msm_fb_data_type *mfd,
+ struct mdp_dither_cfg_data *config)
+{
+ u32 disp_num;
+ int ret = 0;
+ struct mdp_pp_cache_res res_cache;
+
+ ret = pp_validate_dspp_mfd_block(mfd, config->block);
+ if (ret) {
+ pr_err("Invalid block %d mfd index %d, ret %d\n",
+ config->block,
+ (mfd ? mfd->index : -1), ret);
+ return ret;
+ }
+
+ if (config->flags & MDP_PP_OPS_READ) {
+ pr_err("Dither read is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&mdss_pp_mutex);
+ disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0;
+ res_cache.block = DSPP;
+ res_cache.mdss_pp_res = mdss_pp_res;
+ if (pp_ops[PA_DITHER].pp_set_config) {
+ pr_debug("version of pa dither is %d\n", config->version);
+ ret = pp_pa_dither_cache_params(config, &res_cache);
+ if (ret) {
+ pr_err("pa dither config failed version %d ret %d\n",
+ config->version, ret);
+ goto dither_config_exit;
+ }
+ } else {
+ ret = -EINVAL;
+ goto dither_config_exit;
+ }
+ mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PA_DITHER;
+dither_config_exit:
+ mutex_unlock(&mdss_pp_mutex);
+ return ret;
+}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.h b/drivers/video/fbdev/msm/mdss_mdp_pp.h
index c5322d205bf5..9a6e9ea3300c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.h
@@ -99,7 +99,7 @@ struct mdp_pp_feature_ops {
};
struct mdp_pp_driver_ops {
- struct mdp_pp_feature_ops pp_ops[PP_FEATURE_MAX];
+ struct mdp_pp_feature_ops pp_ops[PP_MAX_FEATURES];
void (*pp_opmode_config)(int location, struct pp_sts_type *pp_sts,
u32 *opmode, int side);
int (*get_hist_offset)(u32 block, u32 *ctl_off);
@@ -108,6 +108,13 @@ struct mdp_pp_driver_ops {
void (*gamut_clk_gate_en)(char __iomem *base_addr);
};
+struct mdp_pa_dither_res_data_v1_7 {
+ uint32_t matrix_sz;
+ uint32_t matrix_data[MDP_DITHER_DATA_V1_7_SZ];
+ uint32_t strength;
+ uint32_t offset_en;
+};
+
struct mdss_pp_res_type_v1_7 {
u32 pgc_lm_table_c0[MDSS_BLOCK_DISP_NUM][PGC_LUT_ENTRIES];
u32 pgc_lm_table_c1[MDSS_BLOCK_DISP_NUM][PGC_LUT_ENTRIES];
@@ -128,6 +135,7 @@ struct mdss_pp_res_type_v1_7 {
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];
struct mdp_pa_data_v1_7 pa_v17_data[MDSS_BLOCK_DISP_NUM];
+ struct mdp_pa_dither_res_data_v1_7 pa_dither_data[MDSS_BLOCK_DISP_NUM];
};
struct mdss_pp_res_type {
@@ -156,6 +164,7 @@ struct mdss_pp_res_type {
uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE];
u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE];
struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM];
+ struct mdp_dither_cfg_data pa_dither_cfg[MDSS_BLOCK_DISP_NUM];
/* physical info */
struct pp_hist_col_info *dspp_hist;
/*
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 d1b3f1a89812..ec9c1800787f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
@@ -1476,3 +1476,106 @@ exit:
pp_info->pcc_cfg_data.cfg_payload = cfg_payload;
return ret;
}
+
+static int pp_pa_dither_cache_params_v1_7(
+ struct mdp_dither_cfg_data *config,
+ struct mdss_pp_res_type *mdss_pp_res)
+{
+ struct mdss_pp_res_type_v1_7 *res_cache;
+ int disp_num, ret = 0;
+ size_t sz = 0;
+ struct mdp_pa_dither_res_data_v1_7 *res_data;
+ struct mdp_pa_dither_data dither_data;
+
+ 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 || !mdss_pp_res->pp_data_v1_7) {
+ pr_err("invalid param mdss_pp_res %p pp_data_res %p\n",
+ mdss_pp_res,
+ ((mdss_pp_res) ? mdss_pp_res->pp_data_v1_7 : NULL));
+ return -EINVAL;
+ }
+
+ res_cache = mdss_pp_res->pp_data_v1_7;
+ if (config->flags & MDP_PP_OPS_READ) {
+ pr_err("Read op is not supported\n");
+ return -EINVAL;
+ }
+
+ disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
+ res_data = &res_cache->pa_dither_data[disp_num];
+ if ((config->flags & MDP_PP_OPS_DISABLE) ||
+ !(config->flags & MDP_PP_OPS_WRITE)) {
+ mdss_pp_res->pa_dither_cfg[disp_num] = *config;
+ mdss_pp_res->pa_dither_cfg[disp_num].cfg_payload =
+ (void *) res_data;
+ return 0;
+ }
+ sz = offsetof(struct mdp_pa_dither_data, offset_en)
+ + sizeof(dither_data.offset_en);
+ memset(&dither_data, 0, sizeof(dither_data));
+ ret = copy_from_user(&dither_data, config->cfg_payload, sz);
+ if (ret) {
+ pr_err("failed to copy the dither data ret %d sz %zd", ret, sz);
+ ret = -EFAULT;
+ goto exit;
+ }
+ if (dither_data.matrix_sz != MDP_DITHER_DATA_V1_7_SZ) {
+ pr_err("invalid matrix len %d expected %d\n",
+ dither_data.matrix_sz, MDP_DITHER_DATA_V1_7_SZ);
+ ret = -EINVAL;
+ goto exit;
+ }
+ res_data->offset_en = dither_data.offset_en;
+ res_data->strength = dither_data.strength;
+ res_data->matrix_sz = MDP_DITHER_DATA_V1_7_SZ;
+ ret = copy_from_user(res_data->matrix_data,
+ (u8 *)dither_data.matrix_data,
+ (MDP_DITHER_DATA_V1_7_SZ * sizeof(u32)));
+ if (ret) {
+ pr_err("failed to copy the dither matrix ret %d sz %zd", ret,
+ MDP_DITHER_DATA_V1_7_SZ * sizeof(u32));
+ ret = -EFAULT;
+ goto exit;
+ }
+ mdss_pp_res->pa_dither_cfg[disp_num] = *config;
+ mdss_pp_res->pa_dither_cfg[disp_num].cfg_payload =
+ (void *) res_data;
+exit:
+ return ret;
+}
+
+int pp_pa_dither_cache_params(struct mdp_dither_cfg_data *config,
+ struct mdp_pp_cache_res *res_cache)
+{
+ int ret = 0;
+
+ if (!config || !res_cache) {
+ pr_err("invalid params config %p res_cache %p\n",
+ config, res_cache);
+ return -EINVAL;
+ }
+ if (!res_cache->mdss_pp_res && !res_cache->pipe_res) {
+ pr_err("NULL payload for block %d mdss_pp_res %p pipe_res %p\n",
+ res_cache->block, res_cache->mdss_pp_res,
+ res_cache->pipe_res);
+ return -EINVAL;
+ }
+ switch (config->version) {
+ case mdp_dither_pa_v1_7:
+ if (res_cache->block == DSPP)
+ ret = pp_pa_dither_cache_params_v1_7(config,
+ res_cache->mdss_pp_res);
+ else
+ ret = -ENOTSUPP;
+ break;
+ default:
+ 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 13f14f2673e7..242aad3848c2 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2016, 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
@@ -48,4 +48,7 @@ int pp_copy_layer_igc_payload(struct mdp_overlay_pp_params *pp_info);
int pp_copy_layer_hist_lut_payload(struct mdp_overlay_pp_params *pp_info);
int pp_copy_layer_pa_payload(struct mdp_overlay_pp_params *pp_info);
int pp_copy_layer_pcc_payload(struct mdp_overlay_pp_params *pp_info);
+int pp_pa_dither_cache_params(struct mdp_dither_cfg_data *config,
+ struct mdp_pp_cache_res *res_cache);
+
#endif
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c
index 4920c5387e2c..3d277ae5db5f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c
@@ -64,6 +64,8 @@ static u32 dither_matrix[DITHER_MATRIX_LEN] = {
static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = {
0, 0, 0, 0, 0, 1, 2, 3, 3};
+#define PA_DITHER_REG_OFF 0x2C
+
/* histogram prototypes */
static int pp_get_hist_offset(u32 block, u32 *ctl_off);
static int pp_hist_set_config(char __iomem *base_addr,
@@ -82,13 +84,7 @@ 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);
-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);
-
+/* 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,
@@ -96,22 +92,32 @@ static int pp_dither_set_config(char __iomem *base_addr,
u32 block_type);
static int pp_dither_get_version(u32 *version);
-static void pp_opmode_config(int location, struct pp_sts_type *pp_sts,
- u32 *opmode, int side);
-
+/* 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);
+
+static void pp_opmode_config(int location, struct pp_sts_type *pp_sts,
+ u32 *opmode, int side);
+
void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops)
{
void *pp_cfg = NULL;
@@ -144,6 +150,10 @@ void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops)
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;
+
/* Set opmode pointers */
ops->pp_opmode_config = pp_opmode_config;
@@ -817,3 +827,81 @@ static void pp_pa_opmode_config(char __iomem *base_addr,
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 %p cfg_data %p pp_sts_type %p\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;
+}