Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64ac3318 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mdss: Update histogram and PA LUT in mdss V3"

parents 682dbfa5 db59dc7a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -618,6 +618,7 @@ struct pp_sts_type {
	u32 gamut_sts;
	u32 pgc_sts;
	u32 sharp_sts;
	u32 hist_sts;
	u32 side_sts;
};

+46 −18
Original line number Diff line number Diff line
@@ -407,7 +407,8 @@ static struct mdss_pp_res_type *mdss_pp_res;

static u32 pp_hist_read(char __iomem *v_addr,
				struct pp_hist_col_info *hist_info);
static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix);
static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
				struct pp_sts_type *pp_sts);
static int pp_hist_disable(struct pp_hist_col_info *hist_info);
static void pp_update_pcc_regs(char __iomem *addr,
				struct mdp_pcc_cfg_data *cfg_ptr);
@@ -1048,7 +1049,8 @@ static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op)
	}

	/* Histogram collection enabled checked inside pp_hist_setup */
	pp_hist_setup(op, MDSS_PP_SSPP_CFG | pipe->num, pipe->mixer_left);
	pp_hist_setup(op, MDSS_PP_SSPP_CFG | pipe->num, pipe->mixer_left,
			&pipe->pp_res.pp_sts);

	if (!(pipe->flags & MDP_OVERLAY_PP_CFG_EN)) {
		pr_debug("Overlay PP CFG enable not set\n");
@@ -2002,11 +2004,12 @@ static char __iomem *mdss_mdp_get_dspp_addr_off(u32 dspp_num)
}

/* Assumes that function will be called from within clock enabled space*/
static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
			struct pp_sts_type *pp_sts)
{
	int ret = -EINVAL;
	int ret = 0;
	char __iomem *base;
	u32 op_flags;
	u32 op_flags = 0, block_type = 0;
	struct mdss_mdp_pipe *pipe;
	struct pp_hist_col_info *hist_info;
	unsigned long flag;
@@ -2019,6 +2022,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
	intr_mask = 1;
	if (mix && (PP_LOCAT(block) == MDSS_PP_DSPP_CFG)) {
		/* HIST_EN */
		block_type = DSPP;
		op_flags = BIT(16);
		hist_info = &mdss_pp_res->dspp_hist[mix->num];
		base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block));
@@ -2029,6 +2033,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
	} else if (PP_LOCAT(block) == MDSS_PP_SSPP_CFG &&
		(pp_driver_ops.is_sspp_hist_supp) &&
		(pp_driver_ops.is_sspp_hist_supp())) {
		block_type = SSPP_VIG;
		pipe = __get_hist_pipe(PP_BLOCK(block));
		if (IS_ERR_OR_NULL(pipe)) {
			pr_debug("pipe DNE (%d)\n",
@@ -2041,18 +2046,38 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
		base = pipe->base;
		mdss_mdp_pipe_unmap(pipe);
	} else {
		ret = -EINVAL;
		goto error;
	}

	mutex_lock(&hist_info->hist_mutex);
	spin_lock_irqsave(&hist_info->hist_lock, flag);
	if (hist_info->col_en) {
	/*
	 * Set histogram interrupt if histogram collection is enabled. The
	 * interrupt register offsets are the same across different mdss
	 * versions so far, hence mdss_mdp_hist_irq_set_mask is used for
	 * all the mdss versions.
	 */
	if (hist_info->col_en)
		mdss_mdp_hist_irq_set_mask(intr_mask << hist_info->intr_shift);
	/*
	 * Starting from msmcobalt, the histogram enable bit has been moved
	 * from DSPP opmode register to PA_HIST opmode register, hence we need
	 * to update the histogram enable bit differently based on mdss version.
	 * If HIST pp_set_config is defined, we will enable or disable the
	 * hist_en bit in PA_HIST opmode register inside HIST pp_set_config
	 * function; else, we only need to add the hist_en bit to the *op when
	 * histogram collection is enable, and *op will be passed to
	 * pp_dspp_setup to update the DSPP opmode register.
	 */
	if (pp_ops[HIST].pp_set_config)
		ret = pp_ops[HIST].pp_set_config(base, pp_sts, hist_info,
							block_type);
	else if (hist_info->col_en)
		*op |= op_flags;
	}

	spin_unlock_irqrestore(&hist_info->hist_lock, flag);
	mutex_unlock(&hist_info->hist_mutex);
	ret = 0;
error:
	return ret;
}
@@ -2195,14 +2220,20 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
			(pp_driver_ops.gamut_clk_gate_en))
		pp_driver_ops.gamut_clk_gate_en(base +
					mdata->pp_block_off.dspp_gamut_off);
	ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer);

	if (disp_num < MDSS_BLOCK_DISP_NUM) {
		pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
		pp_sts->side_sts = side;

		ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer,
				pp_sts);
		if (ret)
			goto dspp_exit;

	if (disp_num < MDSS_BLOCK_DISP_NUM)
		flags = mdss_pp_res->pp_disp_flags[disp_num];
	else
	} else {
		flags = 0;
	}

	mixer_cnt = mdss_mdp_get_ctl_mixers(disp_num, mixer_id);
	if (dspp_num < mdata->nad_cfgs && disp_num < mdata->nad_cfgs &&
@@ -2218,9 +2249,6 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
	if ((!flags) && (!(opmode)) && (!ad_flags))
		goto dspp_exit;

	pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
	pp_sts->side_sts = side;

	if (flags & PP_FLAGS_DIRTY_PA) {
		if (!pp_ops[PA].pp_set_config) {
			if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) {
@@ -2847,7 +2875,7 @@ int mdss_mdp_pp_init(struct device *dev)
				vig[i].pp_res.hist.intr_shift = 10;
			if (pp_driver_ops.get_hist_offset) {
				ret = pp_driver_ops.get_hist_offset(
					DSPP, &ctl_off);
					SSPP_VIG, &ctl_off);
				if (ret) {
					pr_err("get_hist_offset ret %d\n",
						ret);
@@ -4803,7 +4831,7 @@ hist_stop_clk:
}

/**
 * mdss_mdp_hist_intr_req() - Request changes the histogram interupts
 * mdss_mdp_hist_intr_req() - Request changes the histogram interrupts
 * @intr: structure containting state of interrupt register
 * @bits: the bits on interrupt register that should be changed
 * @en: true if bits should be set, false if bits should be cleared
+3 −2
Original line number Diff line number Diff line
@@ -421,7 +421,7 @@ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data,
		hist_addr += 4;
	}
	if (copy_to_user(lut_data->data, data, sz)) {
		pr_err("faild to copy the hist_lut back to user\n");
		pr_err("failed to copy the hist_lut back to user\n");
		ret = -EFAULT;
	}
	kfree(data);
@@ -503,7 +503,8 @@ static int pp_hist_lut_set_config(char __iomem *base_addr,
	}
	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;

	writel_relaxed(1, swap_addr);

+304 −2
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@
#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 */
@@ -56,6 +56,24 @@
#define PA_LUTV_REG_OFF 0x200
#define PA_HIST_RAM_REG_OFF 0x400

/* 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);

static int pp_pa_set_config(char __iomem *base_addr,
		struct pp_sts_type *pp_sts, void *cfg_data,
		u32 block_type);
@@ -103,6 +121,16 @@ void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops)
	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;
@@ -111,10 +139,278 @@ void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops)
	/* Set opmode pointers */
	ops->pp_opmode_config = pp_opmode_config;

	ops->get_hist_offset = pp_get_hist_offset;
	ops->gamut_clk_gate_en = NULL;

	return pp_cfg;
}

static int pp_get_hist_offset(u32 block, u32 *ctl_off)
{
	int ret = 0;

	if (!ctl_off) {
		pr_err("invalid params ctl_off %p\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 %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;
	}

	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 %p cfg_data %p\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 = NULL;
	struct mdp_hist_lut_data *lut_cfg_data = NULL;

	if (!base_addr || !cfg_data) {
		pr_err("invalid params base_addr %p cfg_data %p\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 %p\n",
		       lut_cfg_data->version, lut_cfg_data->cfg_payload);
		return -EINVAL;
	}
	lut_data = lut_cfg_data->cfg_payload;
	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 %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;
	}

	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 %p\n", lut_data);
		return -EINVAL;
	}

	if (lut_data->len != ENHIST_LUT_ENTRIES || !lut_data->data) {
		pr_err("invalid hist_lut len %d data %p\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 %p\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 %p pp_sts_type %p\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)
@@ -427,9 +723,15 @@ static void pp_pa_opmode_config(char __iomem *base_addr,
			opmode |= BIT(17);
	}

	/* TODO: reset hist_en, hist_lutv_en and hist_lutv_first_en
	/* 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);
}