msm: mdss: Add support for IGC v3 feature

IGC module is updated for mdss V3 version, which needs to be exposed
to user-space. Change adds interface support to expose the IGC module to
user-space clients.

Change-Id: I159bca8278a1638286cc2392bb5b059e067dbb1b
Signed-off-by: Gopikrishnaiah Anandan <agopik@codeaurora.org>
Signed-off-by: Ping Li <pingli@codeaurora.org>
This commit is contained in:
Gopikrishnaiah Anandan 2016-03-02 17:31:33 -08:00 committed by Kyle Yan
parent 68f301791a
commit c19f4224ab
5 changed files with 469 additions and 20 deletions

View file

@ -2315,6 +2315,10 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
&mdss_pp_res->igc_disp_cfg[disp_num],
DSPP);
}
if (pp_driver_ops.igc_set_dither_strength)
pp_driver_ops.igc_set_dither_strength(base, pp_sts,
&mdss_pp_res->igc_disp_cfg[disp_num],
DSPP);
}
if (flags & PP_FLAGS_DIRTY_ENHIST) {
@ -7454,16 +7458,25 @@ static int pp_get_driver_ops(struct mdp_pp_driver_ops *ops)
break;
case MDSS_MDP_HW_REV_300:
case MDSS_MDP_HW_REV_301:
pp_cfg = pp_get_driver_ops_v3(ops);
/*
* Some of the REV_300 PP features are same as REV_107.
* Get the driver ops for both the versions and update the
* payload/function pointers.
*/
mdss_pp_res->pp_data_v1_7 = NULL;
mdss_pp_res->pp_data_v3 = NULL;
pp_cfg = pp_get_driver_ops_v1_7(ops);
if (IS_ERR_OR_NULL(pp_cfg)) {
ret = -EINVAL;
break;
}
mdss_pp_res->pp_data_v1_7 = pp_cfg;
pp_cfg = pp_get_driver_ops_v3(ops);
if (IS_ERR_OR_NULL(pp_cfg)) {
mdss_pp_res->pp_data_v1_7 = NULL;
ret = -EINVAL;
} else {
mdss_pp_res->pp_data_v1_7 = pp_cfg;
/* Currently all caching data is used from v17 for V3
* hence setting the pointer to NULL. Will be used if we
* have to add any caching specific to V3.
*/
mdss_pp_res->pp_data_v3 = NULL;
mdss_pp_res->pp_data_v3 = pp_cfg;
}
break;
default:

View file

@ -106,6 +106,9 @@ struct mdp_pp_driver_ops {
int (*get_hist_isr_info)(u32 *isr_mask);
bool (*is_sspp_hist_supp)(void);
void (*gamut_clk_gate_en)(char __iomem *base_addr);
int (*igc_set_dither_strength)(char __iomem *base_addr,
struct pp_sts_type *pp_sts, void *cfg_data,
u32 block_type);
};
struct mdp_pa_dither_res_data_v1_7 {
@ -138,6 +141,23 @@ struct mdss_pp_res_type_v1_7 {
struct mdp_pa_dither_res_data_v1_7 pa_dither_data[MDSS_BLOCK_DISP_NUM];
};
struct mdp_igc_lut_data_config {
uint32_t table_fmt;
uint32_t len;
uint32_t *c0_c1_data;
uint32_t *c2_data;
uint32_t strength;
};
struct mdss_pp_res_type_v3 {
int (*igc_set_config)(char __iomem *base_addr,
struct pp_sts_type *pp_sts, void *cfg_data,
u32 block_type);
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_config igc_v3_data[MDSS_BLOCK_DISP_NUM];
};
struct mdss_pp_res_type {
/* logical info */
u32 pp_disp_flags[MDSS_BLOCK_DISP_NUM];

View file

@ -94,6 +94,15 @@ static u32 pp_igc_srgb[IGC_LUT_ENTRIES] = {
3809, 3844, 3879, 3915, 3950, 3986, 4022, 4059, 4095
};
static int pp_igc_lut_cache_params_v3(struct mdp_igc_lut_data *config,
struct mdss_pp_res_type *mdss_pp_res,
u32 copy_from_kernel);
static int pp_igc_lut_cache_params_pipe_v3(struct mdp_igc_lut_data *config,
struct mdss_mdp_pipe *pipe,
u32 copy_from_kernel);
static int pp_hist_lut_cache_params_v1_7(struct mdp_hist_lut_data *config,
struct mdss_pp_res_type *mdss_pp_res)
{
@ -991,6 +1000,21 @@ int pp_igc_lut_cache_params(struct mdp_igc_lut_data *config,
ret);
}
break;
case mdp_igc_v3:
if (res_cache->block == DSPP) {
ret = pp_igc_lut_cache_params_v3(config,
res_cache->mdss_pp_res, copy_from_kernel);
if (ret)
pr_err("cache DSPP IGC params fail ret %d version %d\n",
ret, config->version);
} else {
ret = pp_igc_lut_cache_params_pipe_v3(config,
res_cache->pipe_res, copy_from_kernel);
if (ret)
pr_err("cache SSPP IGC params fail ret %d version %d\n",
ret, config->version);
}
break;
default:
pr_err("unsupported igc version %d\n",
config->version);
@ -1329,6 +1353,7 @@ int pp_copy_layer_igc_payload(struct mdp_overlay_pp_params *pp_info)
{
void *cfg_payload = NULL;
int ret = 0;
size_t sz = 0;
switch (pp_info->igc_cfg.version) {
case mdp_igc_v1_7:
@ -1343,22 +1368,34 @@ int pp_copy_layer_igc_payload(struct mdp_overlay_pp_params *pp_info)
ret = copy_from_user(cfg_payload,
pp_info->igc_cfg.cfg_payload,
sizeof(struct mdp_igc_lut_data_v1_7));
if (ret) {
pr_err("layer list copy from user failed, IGC cfg payload = %p\n",
pp_info->igc_cfg.cfg_payload);
ret = -EFAULT;
kfree(cfg_payload);
cfg_payload = NULL;
break;
case mdp_igc_v3:
cfg_payload = kmalloc(
sizeof(struct mdp_igc_lut_data_payload),
GFP_KERNEL);
if (!cfg_payload) {
ret = -ENOMEM;
goto exit;
}
sz = offsetof(struct mdp_igc_lut_data_payload, strength) +
sizeof(u32);
ret = copy_from_user(cfg_payload,
pp_info->igc_cfg.cfg_payload, sz);
break;
default:
pr_debug("No version set, fallback to legacy IGC version\n");
cfg_payload = NULL;
break;
}
exit:
if (ret) {
pr_err("layer list copy from user failed, IGC cfg payload = %p\n",
pp_info->igc_cfg.cfg_payload);
ret = -EFAULT;
kfree(cfg_payload);
cfg_payload = NULL;
goto exit;
}
pp_info->igc_cfg.cfg_payload = cfg_payload;
return ret;
}
@ -1579,3 +1616,257 @@ int pp_pa_dither_cache_params(struct mdp_dither_cfg_data *config,
return ret;
}
static int pp_igc_lut_cache_params_v3(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_v3 *res_cache;
struct mdp_igc_lut_data_payload v3_usr_config;
struct mdp_igc_lut_data_config *v3_cache_data, *v3_kernel_data = NULL;
u32 disp_num, len = 0;
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_v3) {
pr_err("invalid pp_data_v3 %p\n", mdss_pp_res->pp_data_v3);
return -EINVAL;
}
if (config->ops & MDP_PP_OPS_READ) {
pr_err("read op is not supported\n");
return -EINVAL;
}
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
if (!(config->ops & MDP_PP_OPS_WRITE)) {
pr_debug("op for igc %d\n", config->ops);
mdss_pp_res->igc_disp_cfg[disp_num] = *config;
goto igc_config_exit;
}
res_cache = mdss_pp_res->pp_data_v3;
v3_cache_data = &res_cache->igc_v3_data[disp_num];
if (!v3_cache_data->c0_c1_data || !v3_cache_data->c2_data) {
pr_err("invalid payload c0_c1_data %p c2_data %p\n",
v3_cache_data->c0_c1_data, v3_cache_data->c2_data);
goto igc_config_exit;
}
if (!copy_from_kernel) {
if (copy_from_user(&v3_usr_config,
config->cfg_payload,
sizeof(v3_usr_config))) {
pr_err("failed to copy igc config\n");
ret = -EFAULT;
goto igc_config_exit;
}
len = v3_usr_config.len;
} else {
if (!config->cfg_payload) {
pr_err("can't copy config info NULL payload\n");
ret = -EINVAL;
goto igc_config_exit;
}
v3_kernel_data = config->cfg_payload;
len = v3_kernel_data->len;
}
if (copy_from_kernel && (!v3_kernel_data->c0_c1_data ||
!v3_kernel_data->c2_data)) {
pr_err("copy from kernel invalid params c0_c1_data %p c2_data %p\n",
v3_kernel_data->c0_c1_data,
v3_kernel_data->c2_data);
ret = -EINVAL;
goto igc_config_exit;
}
if (len != IGC_LUT_ENTRIES) {
pr_err("Invalid table size %d exp %d\n",
len, IGC_LUT_ENTRIES);
ret = -EINVAL;
goto igc_config_exit;
}
if (copy_from_kernel) {
memcpy(v3_cache_data->c0_c1_data,
v3_kernel_data->c0_c1_data,
len * sizeof(u32));
memcpy(v3_cache_data->c2_data, v3_kernel_data->c2_data,
len * sizeof(u32));
v3_cache_data->len = len;
v3_cache_data->strength = v3_kernel_data->strength;
v3_cache_data->table_fmt = v3_kernel_data->table_fmt;
} else {
ret = copy_from_user(v3_cache_data->c0_c1_data,
(u8 *)v3_usr_config.c0_c1_data,
len * sizeof(u32));
if (ret) {
pr_err("copy from user failed for c0_c1_data size %zd ret %d\n",
len * sizeof(u32), ret);
ret = -EFAULT;
goto igc_config_exit;
}
ret = copy_from_user(v3_cache_data->c2_data,
(u8 *)v3_usr_config.c2_data,
len * sizeof(u32));
if (ret) {
pr_err("copy from user failed for c2_data size %zd ret %d\n",
len * sizeof(u32), ret);
ret = -EFAULT;
goto igc_config_exit;
}
v3_cache_data->len = len;
v3_cache_data->strength = v3_usr_config.strength;
v3_cache_data->table_fmt = v3_usr_config.table_fmt;
}
mdss_pp_res->igc_disp_cfg[disp_num] = *config;
mdss_pp_res->igc_disp_cfg[disp_num].cfg_payload =
(void *)v3_cache_data;
igc_config_exit:
return ret;
}
static int pp_igc_lut_cache_params_pipe_v3(
struct mdp_igc_lut_data *config, struct mdss_mdp_pipe *pipe,
u32 copy_from_kernel)
{
struct mdp_igc_lut_data_config *v3_cache_data = NULL;
struct mdp_igc_lut_data_payload v3_usr_config;
u32 *c0_c1_data, *c2_data, len;
int ret = 0, fix_up = 0, i = 0;
u32 table_fmt = mdp_igc_rec_max, strength = 0;
if (!config || !pipe) {
pr_err("invalid param config %p pipe %p\n",
config, pipe);
return -EINVAL;
}
if (config->ops & MDP_PP_OPS_READ) {
pr_err("read op is not supported\n");
return -EINVAL;
}
if (!(config->ops & MDP_PP_OPS_WRITE)) {
pr_debug("op for gamut %d\n", config->ops);
goto igc_config_exit;
}
if (!config->cfg_payload) {
pr_err("can't copy config info NULL payload\n");
ret = -EINVAL;
goto igc_config_exit;
}
v3_cache_data = pipe->pp_res.igc_cfg_payload;
if (!v3_cache_data)
v3_cache_data = kzalloc(sizeof(*v3_cache_data), GFP_KERNEL);
if (!v3_cache_data) {
ret = -ENOMEM;
goto igc_config_exit;
} else {
pipe->pp_res.igc_cfg_payload = v3_cache_data;
pipe->pp_cfg.igc_cfg.cfg_payload = v3_cache_data;
}
if (copy_from_kernel) {
memcpy(v3_cache_data, config->cfg_payload,
sizeof(*v3_cache_data));
table_fmt = v3_cache_data->table_fmt;
len = v3_cache_data->len;
strength = 0;
} else {
memcpy(&v3_usr_config, config->cfg_payload,
sizeof(v3_usr_config));
table_fmt = v3_usr_config.table_fmt;
len = v3_usr_config.len;
strength = v3_usr_config.strength;
}
switch (table_fmt) {
case mdp_igc_custom:
if (len != IGC_LUT_ENTRIES) {
pr_err("invalid igc len %d exp %d\n", len,
IGC_LUT_ENTRIES);
ret = -EINVAL;
goto igc_config_exit;
}
if (!copy_from_kernel)
break;
c0_c1_data = v3_cache_data->c0_c1_data;
c2_data = v3_cache_data->c2_data;
if (!c0_c1_data || !c2_data) {
pr_err("invalid param c0_c1_data %p c2_data %p\n",
c0_c1_data, c2_data);
ret = -EINVAL;
goto igc_config_exit;
}
break;
case mdp_igc_rec709:
c0_c1_data = pp_igc_709;
c2_data = pp_igc_709;
v3_usr_config.len = IGC_LUT_ENTRIES;
copy_from_kernel = 1;
fix_up = 1;
break;
case mdp_igc_srgb:
c0_c1_data = pp_igc_srgb;
c2_data = pp_igc_srgb;
v3_usr_config.len = IGC_LUT_ENTRIES;
copy_from_kernel = 1;
fix_up = 1;
break;
case mdp_igc_rec601:
c0_c1_data = pp_igc_601;
c2_data = pp_igc_601;
v3_usr_config.len = IGC_LUT_ENTRIES;
copy_from_kernel = 1;
fix_up = 1;
break;
default:
pr_err("invalid format %d\n", table_fmt);
ret = -EINVAL;
goto igc_config_exit;
}
v3_cache_data->c0_c1_data = pipe->pp_res.igc_c0_c1;
v3_cache_data->c2_data = pipe->pp_res.igc_c2;
v3_cache_data->len = IGC_LUT_ENTRIES;
v3_cache_data->strength = strength;
v3_cache_data->table_fmt = table_fmt;
if (copy_from_kernel) {
memcpy(v3_cache_data->c0_c1_data, c0_c1_data,
IGC_LUT_ENTRIES * sizeof(u32));
memcpy(v3_cache_data->c2_data, c2_data,
IGC_LUT_ENTRIES * sizeof(u32));
if (fix_up) {
for (i = 0; i < IGC_LUT_ENTRIES; i++)
v3_cache_data->c0_c1_data[i]
|= (v3_cache_data->c0_c1_data[i]
<< IGC_C1_SHIFT);
}
} else {
if (copy_from_user(v3_cache_data->c0_c1_data,
(u8 *)v3_usr_config.c0_c1_data,
IGC_LUT_ENTRIES * sizeof(u32))) {
pr_err("error in copying the c0_c1_data of size %zd\n",
IGC_LUT_ENTRIES * sizeof(u32));
ret = -EFAULT;
goto igc_config_exit;
}
if (copy_from_user(v3_cache_data->c2_data,
(u8 *)v3_usr_config.c2_data,
IGC_LUT_ENTRIES * sizeof(u32))) {
pr_err("error in copying the c2_data of size %zd\n",
IGC_LUT_ENTRIES * sizeof(u32));
ret = -EFAULT;
}
}
igc_config_exit:
if (ret || (config->ops & MDP_PP_OPS_DISABLE)) {
kfree(v3_cache_data);
pipe->pp_cfg.igc_cfg.cfg_payload = NULL;
pipe->pp_res.igc_cfg_payload = NULL;
}
return ret;
}

View file

@ -66,6 +66,8 @@ static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = {
#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,
@ -115,21 +117,53 @@ 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)
{
void *pp_cfg = NULL;
if (!ops) {
pr_err("PP driver ops invalid %p\n", ops);
return ERR_PTR(-EINVAL);
}
pp_cfg = pp_get_driver_ops_v1_7(ops);
if (IS_ERR_OR_NULL(pp_cfg))
return NULL;
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;
@ -154,13 +188,18 @@ void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops)
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 pp_cfg;
return &config_data;
}
static int pp_get_hist_offset(u32 block, u32 *ctl_off)
@ -905,3 +944,79 @@ dither_set_sts:
}
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 %p cfg_data %p 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 %p pp_sts %p cfg_data %p igc_set_config %p\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;
}

View file

@ -622,6 +622,14 @@ struct mdp_igc_lut_data_v1_7 {
uint32_t *c2_data;
};
struct mdp_igc_lut_data_payload {
uint32_t table_fmt;
uint32_t len;
uint64_t __user c0_c1_data;
uint64_t __user c2_data;
uint32_t strength;
};
struct mdp_histogram_cfg {
uint32_t ops;
uint32_t block;
@ -1407,11 +1415,13 @@ enum {
mdp_pcc_vmax,
mdp_pp_legacy,
mdp_dither_pa_v1_7,
mdp_igc_v3,
mdp_pp_unknown = 255
};
#define mdp_dither_pa_v1_7 mdp_dither_pa_v1_7
#define mdp_pp_unknown mdp_pp_unknown
#define mdp_igc_v3 mdp_igc_v3
/* PP Features */
enum {