// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2014-2018, 2020, The Linux Foundation. All rights reserved.
 *
 */

#define pr_fmt(fmt)	"%s: " fmt, __func__

#include <linux/uaccess.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_pp.h"
#include "mdss_mdp_pp_common.h"


/* MDP v1.7 specific macros */

/* PCC_EN for PCC opmode*/
#define PCC_ENABLE	BIT(0)
#define PCC_OP_MODE_OFF 0
#define PCC_CONST_COEFF_OFF 4
#define PCC_R_COEFF_OFF 0x10
#define PCC_G_COEFF_OFF 0x1C
#define PCC_B_COEFF_OFF 0x28
#define PCC_RG_COEFF_OFF 0x34
#define PCC_RB_COEFF_OFF 0x40
#define PCC_GB_COEFF_OFF 0x4C
#define PCC_RGB_COEFF_OFF 0x58
#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
#define GAMUT_TABLE_LOWER_GB 0xC
#define GAMUT_C0_SCALE_OFF 0x10
#define GAMUT_CLK_CTRL 0xD0
#define GAMUT_CLK_STATUS 0xD4
#define GAMUT_READ_TABLE_EN  BIT(16)
#define GAMUT_TABLE_SELECT(x) ((BIT(x)) << 12)
#define GAMUT_COARSE_EN (BIT(2))
#define GAMUT_COARSE_INDEX 1248
#define GAMUT_FINE_INDEX 0
#define GAMUT_MAP_EN BIT(1)
#define GAMUT_ENABLE BIT(0)
#define GAMUT_CLK_GATING_ACTIVE 0x0
#define GAMUT_CLK_GATING_PARTIAL_ACTIVE 0x11
#define GAMUT_CLK_GATING_INACTIVE 0x33

#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)

#define HIST_LUT_VIG_OP_FIRST_EN	BIT(21)
#define HIST_LUT_DSPP_OP_FIRST_EN	BIT(21)
#define HIST_LUT_VIG_OP_ENABLE		BIT(10)
#define HIST_LUT_DSPP_OP_ENABLE		BIT(19)
#define REG_SSPP_VIG_HIST_LUT_BASE	0x1400
#define REG_DSPP_HIST_LUT_BASE		0x1400
#define REG_SSPP_VIG_HIST_SWAP_BASE	0x300
#define REG_DSPP_HIST_SWAP_BASE		0x234
#define ENHIST_LOWER_VALUE_MASK		0x3FF
#define ENHIST_UPPER_VALUE_MASK		0x3FF0000
#define ENHIST_BIT_SHIFT		16

#define PGC_OPMODE_OFF 0
#define PGC_C0_LUT_INDEX 4
#define PGC_INDEX_OFF 4
#define PGC_C1C2_LUT_OFF 8
#define PGC_LUT_SWAP 0x1C
#define PGC_LUT_SEL 0x20
#define PGC_DATA_MASK (BIT(10) - 1)
#define PGC_ODD_SHIFT 16
#define PGC_SWAP 1
#define PGC_8B_ROUND BIT(1)
#define PGC_ENABLE BIT(0)

#define HIST_V3_INTR_BIT_MASK 0xF33333
#define HIST_CTL_OFF_DSPP_V1_7 0x210
#define HIST_CTL_OFF_SSPP_V1_7 0x2C4
#define HIST_DATA_OFF_DSPP_V1_7 0x1000
#define HIST_DATA_OFF_SSPP_V1_7 0xA00
#define HIST_DATA_MASK 0xFFFFFF
#define DITHER_MATRIX_OFF 0x14
#define DITHER_MATRIX_INDEX 16
#define DITHER_DEPTH_MAP_INDEX 9
static u32 dither_matrix[DITHER_MATRIX_INDEX] = {
	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_DSPP_GLOBAL_OFF 0x238
#define PA_DSPP_MEM_COL_SKIN_P0_OFF 0x254
#define PA_DSPP_MEM_COL_SKIN_P2_OFF 0x318
#define PA_DSPP_MEM_COL_SKY_P0_OFF 0x268
#define PA_DSPP_MEM_COL_SKY_P2_OFF 0x320
#define PA_DSPP_MEM_COL_FOL_P0_OFF 0x27C
#define PA_DSPP_MEM_COL_FOL_P2_OFF 0x328
#define PA_SIX_ZONE_LUT_OFF 0x248
#define PA_SIX_ZONE_REGION_OFF 0x250
#define PA_SIX_ZONE_ADJ_OFF 0x330
#define PA_VIG_GLOBAL_OFF 0x310
#define PA_VIG_MEM_COL_SKIN_P0_OFF 0x288
#define PA_VIG_MEM_COL_SKIN_P2_OFF 0x418
#define PA_VIG_MEM_COL_SKY_P0_OFF 0x29C
#define PA_VIG_MEM_COL_SKY_P2_OFF 0x420
#define PA_VIG_MEM_COL_FOL_P0_OFF 0x2B0
#define PA_VIG_MEM_COL_FOL_P2_OFF 0x428
#define PA_DSPP_HOLD_OFF 0x314
#define PA_VIG_HOLD_OFF 0x414
#define PA_GLOBAL_HUE_MASK 0xFFF
#define PA_GLOBAL_SAT_MASK 0xFFFF
#define PA_GLOBAL_VAL_MASK 0xFF
#define PA_GLOBAL_CONT_MASK 0xFF
#define PA_MEM_COL_ADJ_P0_MASK 0xFFFF07FF
#define PA_MEM_COL_HUE_REGION_MASK 0x7FF07FF
#define PA_MEM_COL_SAT_REGION_MASK 0xFFFFFF
#define PA_MEM_COL_VAL_REGION_MASK 0xFFFFFF
#define PA_SIX_ZONE_INDEX_UPDATE BIT(26)
#define PA_SIX_ZONE_VALUE_UPDATE BIT(25)
#define PA_SIX_ZONE_CURVE_P0_MASK 0xFFF
#define PA_SIX_ZONE_CURVE_P1_MASK 0xFFF0FFF
#define PA_SIX_ZONE_ADJ_P0_MASK 0xFFFF
#define PA_HOLD_MASK 0x3
#define PA_HOLD_SAT_SHIFT 0
#define PA_HOLD_VAL_SHIFT 2
#define PA_HOLD_SKIN_SHIFT 0
#define PA_HOLD_SKY_SHIFT 4
#define PA_HOLD_FOL_SHIFT 8
#define PA_HOLD_SIX_ZONE_SHIFT 12
#define PA_HOLD_SKIN_MASK 0xF
#define PA_HOLD_SKY_MASK 0xF0
#define PA_HOLD_FOL_MASK 0xF00
#define PA_HOLD_SIX_ZONE_MASK 0xF000
#define PA_DSPP_OP_ENABLE BIT(20)
#define PA_DSPP_OP_HUE_MASK BIT(25)
#define PA_DSPP_OP_SAT_MASK BIT(26)
#define PA_DSPP_OP_VAL_MASK BIT(27)
#define PA_DSPP_OP_CONT_MASK BIT(28)
#define PA_DSPP_OP_SAT_ZERO_EXP_EN BIT(1)
#define PA_DSPP_OP_SIX_ZONE_HUE_MASK BIT(29)
#define PA_DSPP_OP_SIX_ZONE_SAT_MASK BIT(30)
#define PA_DSPP_OP_SIX_ZONE_VAL_MASK BIT(31)
#define PA_DSPP_OP_MEM_COL_SKIN_MASK BIT(5)
#define PA_DSPP_OP_MEM_COL_FOL_MASK BIT(6)
#define PA_DSPP_OP_MEM_COL_SKY_MASK BIT(7)
#define PA_DSPP_OP_MEM_PROT_HUE_EN BIT(22)
#define PA_DSPP_OP_MEM_PROT_SAT_EN BIT(23)
#define PA_DSPP_OP_MEM_PROT_VAL_EN BIT(24)
#define PA_DSPP_OP_MEM_PROT_CONT_EN BIT(18)
#define PA_DSPP_OP_MEM_PROT_BLEND_EN BIT(3)
#define PA_DSPP_OP_MEM_PROT_SIX_EN BIT(17)
#define PA_VIG_OP_HUE_MASK BIT(25)
#define PA_VIG_OP_SAT_MASK BIT(26)
#define PA_VIG_OP_VAL_MASK BIT(27)
#define PA_VIG_OP_CONT_MASK BIT(28)
#define PA_VIG_OP_MEM_PROT_HUE_EN BIT(12)
#define PA_VIG_OP_MEM_PROT_SAT_EN BIT(13)
#define PA_VIG_OP_MEM_PROT_VAL_EN BIT(14)
#define PA_VIG_OP_MEM_PROT_CONT_EN BIT(15)
#define PA_VIG_OP_MEM_COL_SKIN_MASK BIT(5)
#define PA_VIG_OP_MEM_COL_FOL_MASK BIT(6)
#define PA_VIG_OP_MEM_COL_SKY_MASK BIT(7)
#define PA_VIG_OP_MEM_PROT_BLEND_EN BIT(1)
#define PA_VIG_OP_ENABLE BIT(4)
#define PA_VIG_OP_SAT_ZERO_EXP_EN BIT(2)

static struct mdss_pp_res_type_v1_7 config_data;

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_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);
/* histogram prototypes */
static int pp_hist_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num);
static int pp_get_hist_offset(u32 block, u32 *ctl_off);
static int pp_get_hist_isr(u32 *isr_mask);
static bool pp_is_sspp_hist_supp(void);

static void pp_opmode_config(int location, struct pp_sts_type *pp_sts,
		u32 *opmode, int side);

/* Gamut prototypes */
static int pp_gamut_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num);
static int pp_gamut_set_config(char __iomem *base_addr,
		struct pp_sts_type *pp_sts, void *cfg_data,
		u32 block_type);
/* PCC prototypes */
static int pp_pcc_set_config(char __iomem *base_addr,
			struct pp_sts_type *pp_sts, void *cfg_data,
			u32 block_type);
static int pp_pcc_get_config(char __iomem *base_addr, void *cfg_data,
				u32 block_type, u32 disp_num);
/* 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 void pp_pa_update_dspp_opmode(int pa_sts, u32 *opmode);
static void pp_pa_update_vig_opmode(int pa_sts, u32 *opmode);

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_pgc_set_config(char __iomem *base_addr,
		struct pp_sts_type *pp_sts, void *cfg_data,
		u32 block_type);
static int pp_pgc_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num);
static int pp_pcc_get_version(u32 *version);
static int pp_igc_get_version(u32 *version);
static int pp_pgc_get_version(u32 *version);
static int pp_pa_get_version(u32 *version);
static int pp_gamut_get_version(u32 *version);
static int pp_dither_get_version(u32 *version);
static int pp_hist_lut_get_version(u32 *version);
static void pp_gamut_clock_gating_en(char __iomem *base_addr);

void *pp_get_driver_ops_v1_7(struct mdp_pp_driver_ops *ops)
{
	if (!ops) {
		pr_err("PP driver ops invalid %pK\n", ops);
		return ERR_PTR(-EINVAL);
	}

	/* IGC ops */
	ops->pp_ops[IGC].pp_set_config = pp_igc_set_config;
	ops->pp_ops[IGC].pp_get_config = pp_igc_get_config;
	ops->pp_ops[IGC].pp_get_version = pp_igc_get_version;

	/* PCC ops */
	ops->pp_ops[PCC].pp_set_config = pp_pcc_set_config;
	ops->pp_ops[PCC].pp_get_config = pp_pcc_get_config;
	ops->pp_ops[PCC].pp_get_version = pp_pcc_get_version;
	/* GC ops */
	ops->pp_ops[GC].pp_set_config = pp_pgc_set_config;
	ops->pp_ops[GC].pp_get_config = pp_pgc_get_config;
	ops->pp_ops[GC].pp_get_version = pp_pgc_get_version;

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

	/* Gamut ops */
	ops->pp_ops[GAMUT].pp_set_config = pp_gamut_set_config;
	ops->pp_ops[GAMUT].pp_get_config = pp_gamut_get_config;
	ops->pp_ops[GAMUT].pp_get_version = pp_gamut_get_version;

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

	/* QSEED ops */
	ops->pp_ops[QSEED].pp_set_config = NULL;
	ops->pp_ops[QSEED].pp_get_config = NULL;
	ops->pp_ops[QSEED].pp_get_version = NULL;

	/* 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 = NULL;
	ops->pp_ops[HIST].pp_get_config = pp_hist_get_config;
	ops->pp_ops[HIST].pp_get_version = NULL;

	/* Set opmode pointers */
	ops->pp_opmode_config = pp_opmode_config;

	ops->get_hist_offset = pp_get_hist_offset;
	ops->get_hist_isr_info = pp_get_hist_isr;
	ops->is_sspp_hist_supp = pp_is_sspp_hist_supp;
	ops->gamut_clk_gate_en = pp_gamut_clock_gating_en;
	return &config_data;
}

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_RGB:
		break;
	case SSPP_DMA:
		break;
	case SSPP_VIG:
		if (pp_sts->pa_sts & PP_STS_ENABLE)
			pp_pa_update_vig_opmode(pp_sts->pa_sts, opmode);
		if (pp_sts->enhist_sts & PP_STS_ENABLE) {
			*opmode |= HIST_LUT_VIG_OP_ENABLE |
				  PA_VIG_OP_ENABLE;
			if (pp_sts->enhist_sts & PP_STS_PA_LUT_FIRST)
				*opmode |= HIST_LUT_VIG_OP_FIRST_EN;
		}
		break;
	case DSPP:
		if (pp_sts_is_enabled(pp_sts->pa_sts, side))
			pp_pa_update_dspp_opmode(pp_sts->pa_sts, opmode);
		if (pp_sts_is_enabled(pp_sts->igc_sts, side))
			*opmode |= IGC_DSPP_OP_MODE_EN;
		if (pp_sts->enhist_sts & PP_STS_ENABLE) {
			*opmode |= HIST_LUT_DSPP_OP_ENABLE |
				  PA_DSPP_OP_ENABLE;
			if (pp_sts->enhist_sts & PP_STS_PA_LUT_FIRST)
				*opmode |= HIST_LUT_DSPP_OP_FIRST_EN;
		}
		if (pp_sts_is_enabled(pp_sts->dither_sts, side))
			*opmode |= MDSS_MDP_DSPP_OP_DST_DITHER_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 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_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;
	}

	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\n", 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;
	}

	switch (block_type) {
	case SSPP_VIG:
		hist_addr = base_addr + REG_SSPP_VIG_HIST_LUT_BASE;
		break;
	case DSPP:
		hist_addr = base_addr + REG_DSPP_HIST_LUT_BASE;
		break;
	default:
		pr_err("Invalid block type %d\n", block_type);
		ret = -EINVAL;
		break;
	}

	if (ret) {
		pr_err("Failed to read hist_lut table ret %d\n", ret);
		return ret;
	}

	data = kzalloc(sz, GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	for (i = 0; i < ENHIST_LUT_ENTRIES; i += 2) {
		temp = readl_relaxed(hist_addr);
		data[i] = temp & ENHIST_LOWER_VALUE_MASK;
		data[i + 1] =
			(temp & ENHIST_UPPER_VALUE_MASK) >> ENHIST_BIT_SHIFT;
		hist_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_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;
	}

	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_DISABLE) {
		pr_debug("Disable Hist LUT\n");
		goto bail_out;
	}

	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_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) {
		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;
	}
	switch (block_type) {
	case SSPP_VIG:
		hist_addr = base_addr + REG_SSPP_VIG_HIST_LUT_BASE;
		swap_addr = base_addr +
			REG_SSPP_VIG_HIST_SWAP_BASE;
		break;
	case DSPP:
		hist_addr = base_addr + REG_DSPP_HIST_LUT_BASE;
		swap_addr = base_addr + REG_DSPP_HIST_SWAP_BASE;
		break;
	default:
		pr_err("Invalid block type %d\n", block_type);
		ret = -EINVAL;
		break;
	}
	if (ret) {
		pr_err("hist_lut table not updated ret %d\n", ret);
		return ret;
	}
	for (i = 0; i < ENHIST_LUT_ENTRIES; i += 2) {
		temp = (lut_data->data[i] & ENHIST_LOWER_VALUE_MASK) |
			((lut_data->data[i + 1] & ENHIST_LOWER_VALUE_MASK)
			 << ENHIST_BIT_SHIFT);

		writel_relaxed(temp, hist_addr);
		hist_addr += 4;
	}
	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);

bail_out:
	if (lut_cfg_data->ops & MDP_PP_OPS_DISABLE)
		pp_sts->enhist_sts &= ~PP_STS_ENABLE;
	else if (lut_cfg_data->ops & MDP_PP_OPS_ENABLE)
		pp_sts->enhist_sts |= PP_STS_ENABLE;

	return ret;
}

static int pp_dither_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num)
{
	pr_err("Operation not supported\n");
	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;
	uint32_t *pdata = 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)
		return -ENOTSUPP;
	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) {
		pr_debug("set disable dither\n");
		goto bail_out;
	}

	if (!(dither_cfg_data->flags & MDP_PP_OPS_WRITE)) {
		pr_debug("non write ops set %d\n", dither_cfg_data->flags);
		goto bail_out;
	}

	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) ||
		(dither_data->len > DITHER_MATRIX_INDEX)) {
		pr_err("invalid data for dither, g_y_depth %d y_cb_depth %d r_cr_depth %d\n len %d\n",
			dither_data->g_y_depth, dither_data->b_cb_depth,
			dither_data->r_cr_depth, dither_data->len);
		return -EINVAL;
	}
	if (!dither_data->len)
		pdata = dither_matrix;
	else
		pdata = dither_data->data;

	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 += DITHER_MATRIX_OFF;
	for (i = 0; i < DITHER_MATRIX_INDEX; i += 4) {
		data = pdata[i] | (pdata[i + 1] << 4) |
		       (pdata[i + 2] << 8) | (pdata[i + 3] << 12);
		writel_relaxed(data, base_addr);
		base_addr += 4;
	}
bail_out:
	if (dither_cfg_data->flags & MDP_PP_OPS_DISABLE)
		pp_sts->dither_sts &= ~PP_STS_ENABLE;
	else if (dither_cfg_data->flags & MDP_PP_OPS_ENABLE)
		pp_sts->dither_sts |= PP_STS_ENABLE;
	pp_sts_set_split_bits(&pp_sts->dither_sts, dither_cfg_data->flags);

	return 0;
}

static int pp_hist_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num)
{
	int ret = 0, i = 0;
	u32 sum = 0;
	struct pp_hist_col_info *hist_info = NULL;

	if (!base_addr || !cfg_data) {
		pr_err("invalid params base_addr %pK cfg_data %pK\n",
		       base_addr, cfg_data);
		return -EINVAL;
	}

	hist_info = (struct pp_hist_col_info *) cfg_data;

	switch (block_type) {
	case SSPP_VIG:
		base_addr += HIST_DATA_OFF_SSPP_V1_7;
		break;
	case DSPP:
		base_addr += HIST_DATA_OFF_DSPP_V1_7;
		break;
	default:
		pr_err("Invalid block type %d\n", block_type);
		ret = -EINVAL;
		break;
	}
	if (ret) {
		pr_err("Failed to read hist data ret %d\n", ret);
		return ret;
	}

	for (i = 0; i < HIST_V_SIZE; i++) {
		hist_info->data[i] = readl_relaxed(base_addr) & HIST_DATA_MASK;
		base_addr += 0x4;
		sum += hist_info->data[i];
	}
	hist_info->hist_cnt_read++;
	return sum;
}

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 = HIST_CTL_OFF_SSPP_V1_7;
		break;
	case DSPP:
		*ctl_off = HIST_CTL_OFF_DSPP_V1_7;
		break;
	default:
		pr_err("Invalid block type %d\n", block);
		ret = -EINVAL;
		break;
	}
	return ret;
}

static int pp_get_hist_isr(u32 *isr_mask)
{
	if (!isr_mask) {
		pr_err("invalid params isr_mask %pK\n", isr_mask);
		return -EINVAL;
	}

	*isr_mask = HIST_V3_INTR_BIT_MASK;
	return 0;
}

static bool pp_is_sspp_hist_supp(void)
{
	return false;
}

static int pp_gamut_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num)
{
	u32 val = 0, sz = 0, sz_scale = 0, mode = 0, tbl_sz = 0;
	u32 index_start = 0;
	int i = 0, j = 0, ret = 0;
	u32 *gamut_tbl = NULL, *gamut_c0 = NULL, *gamut_c1c2 = NULL;
	struct mdp_gamut_cfg_data *gamut_cfg = (struct mdp_gamut_cfg_data *)
						cfg_data;
	struct mdp_gamut_data_v1_7 gamut_data;
	u32 clk_gate_disable = 0;

	if (!base_addr || !cfg_data) {
		pr_err("invalid params base_addr %pK cfg_data %pK\n",
		       base_addr, cfg_data);
		return -EINVAL;
	}
	if (gamut_cfg->version != mdp_gamut_v1_7) {
		pr_err("unsupported version of gamut %d\n",
		       gamut_cfg->version);
		return -EINVAL;
	}
	if (copy_from_user(&gamut_data, gamut_cfg->cfg_payload,
			   sizeof(gamut_data))) {
		pr_err("copy from user failed for gamut_data\n");
		return -EFAULT;
	}
	mode = readl_relaxed(base_addr + GAMUT_OP_MODE_OFF);
	if (mode & GAMUT_COARSE_EN) {
		tbl_sz = MDP_GAMUT_TABLE_V1_7_COARSE_SZ;
		sz = tbl_sz * sizeof(u32);
		index_start = GAMUT_COARSE_INDEX;
		gamut_data.mode = mdp_gamut_coarse_mode;
	} else {
		tbl_sz = MDP_GAMUT_TABLE_V1_7_SZ;
		sz = tbl_sz * sizeof(u32);
		index_start = GAMUT_FINE_INDEX;
		gamut_data.mode = mdp_gamut_fine_mode;
	}
	gamut_data.map_en = mode & GAMUT_MAP_EN;
	sz_scale = MDP_GAMUT_SCALE_OFF_SZ * sizeof(u32);
	for (i = 0; i < MDP_GAMUT_TABLE_NUM_V1_7; i++) {
		if (!access_ok(VERIFY_WRITE, gamut_data.c0_data[i], sz)) {
			pr_err("invalid c0 address for sz %d table index %d\n",
				sz, (i+1));
			return -EFAULT;
		}
		if (!access_ok(VERIFY_WRITE, gamut_data.c1_c2_data[i], sz)) {
			pr_err("invalid c1c2 address for sz %d table index %d\n",
				sz, (i+1));
			return -EFAULT;
		}
		gamut_data.tbl_size[i] = tbl_sz;
		if (i < MDP_GAMUT_SCALE_OFF_TABLE_NUM) {
			if (!access_ok(VERIFY_WRITE,
			     gamut_data.scale_off_data[i], sz_scale)) {
				pr_err("invalid scale address for sz %d color c%d\n",
					sz_scale, i);
				return -EFAULT;
			}
			gamut_data.tbl_scale_off_sz[i] =
			MDP_GAMUT_SCALE_OFF_SZ;
		}
	}
	/* allocate for c0 and c1c2 tables */
	gamut_tbl = kzalloc((sz * 2), GFP_KERNEL);
	if (!gamut_tbl)
		return -ENOMEM;

	gamut_c0 = gamut_tbl;
	gamut_c1c2 = gamut_c0 + tbl_sz;
	writel_relaxed(GAMUT_CLK_GATING_INACTIVE, base_addr + GAMUT_CLK_CTRL);
	clk_gate_disable = 1;
	for (i = 0; i < MDP_GAMUT_TABLE_NUM_V1_7; i++) {
		val = index_start;
		val |= GAMUT_READ_TABLE_EN;
		val |= GAMUT_TABLE_SELECT(i);
		writel_relaxed(val, (base_addr + GAMUT_TABLE_INDEX));
		for (j = 0; j < tbl_sz; j++) {
			gamut_c1c2[j] = readl_relaxed(base_addr +
					GAMUT_TABLE_LOWER_GB);
			gamut_c0[j] = readl_relaxed(base_addr +
				      GAMUT_TABLE_UPPER_R);
		}
		if (copy_to_user(gamut_data.c0_data[i], gamut_c0, sz)) {
			pr_err("copy to user failed for table %d c0 sz %d\n",
			       i, sz);
			ret = -EFAULT;
			goto bail_out;
		}
		if (copy_to_user(gamut_data.c1_c2_data[i], gamut_c1c2, sz)) {
			pr_err("copy to user failed for table %d c1c2 sz %d\n",
			       i, sz);
			ret = -EFAULT;
			goto bail_out;
		}
	}
	sz_scale = MDP_GAMUT_SCALE_OFF_TABLE_NUM * MDP_GAMUT_SCALE_OFF_SZ
		   * sizeof(u32);
	if (sz < sz_scale) {
		kfree(gamut_tbl);
		gamut_tbl = kzalloc(sz_scale, GFP_KERNEL);
		if (!gamut_tbl) {
			pr_err("failed to alloc scale tbl size %d\n",
			       sz_scale);
			ret = -ENOMEM;
			goto bail_out;
		}
	}
	gamut_c0 = gamut_tbl;
	base_addr += GAMUT_C0_SCALE_OFF;
	for (i = 0;
	     i < (MDP_GAMUT_SCALE_OFF_TABLE_NUM * MDP_GAMUT_SCALE_OFF_SZ);
	     i++) {
		gamut_c0[i] = readl_relaxed(base_addr);
		base_addr += 4;
	}
	for (i = 0; i < MDP_GAMUT_SCALE_OFF_TABLE_NUM; i++) {
		if (copy_to_user(gamut_data.scale_off_data[i],
				 &gamut_c0[i * MDP_GAMUT_SCALE_OFF_SZ],
				 (MDP_GAMUT_SCALE_OFF_SZ * sizeof(u32)))) {
			pr_err("copy to user failed for scale color c%d\n",
			       i);
			ret = -EFAULT;
			goto bail_out;
		}
	}
	if (copy_to_user(gamut_cfg->cfg_payload, &gamut_data,
			 sizeof(gamut_data))) {
		pr_err("failed to copy the gamut info into payload\n");
		ret = -EFAULT;
	}
bail_out:
	if (clk_gate_disable)
		writel_relaxed(GAMUT_CLK_GATING_PARTIAL_ACTIVE,
					   base_addr + GAMUT_CLK_CTRL);
	kfree(gamut_tbl);
	return ret;
}

static int pp_gamut_set_config(char __iomem *base_addr,
		struct pp_sts_type *pp_sts, void *cfg_data,
		u32 block_type)
{
	int val = 0, ret = 0, i = 0, j = 0;
	u32 index_start = 0, tbl_sz = 0;
	struct mdp_gamut_cfg_data *gamut_cfg_data = NULL;
	struct mdp_gamut_data_v1_7 *gamut_data = NULL;
	char __iomem *base_addr_scale = base_addr;
	uint64_t gamut_val;

	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;
	}
	gamut_cfg_data = (struct mdp_gamut_cfg_data *) cfg_data;
	if (gamut_cfg_data->version != mdp_gamut_v1_7) {
		pr_err("invalid gamut version %d\n", gamut_cfg_data->version);
		return -EINVAL;
	}
	if (!(gamut_cfg_data->flags & ~(MDP_PP_OPS_READ))) {
		pr_debug("only read ops is set %d\n", gamut_cfg_data->flags);
		return 0;
	}

	if (gamut_cfg_data->flags & MDP_PP_OPS_DISABLE) {
		pr_debug("disabling gamut\n");
		goto bail_out;
	}

	gamut_data = (struct mdp_gamut_data_v1_7 *)
		      gamut_cfg_data->cfg_payload;
	if (!gamut_data) {
		pr_err("invalid payload for gamut %pK\n", gamut_data);
		return -EINVAL;
	}

	if (gamut_data->mode != mdp_gamut_fine_mode &&
	    gamut_data->mode != mdp_gamut_coarse_mode) {
		pr_err("invalid gamut mode %d\n", gamut_data->mode);
		return -EINVAL;
	}
	index_start = (gamut_data->mode == mdp_gamut_fine_mode) ?
		       GAMUT_FINE_INDEX : GAMUT_COARSE_INDEX;
	tbl_sz = (gamut_data->mode == mdp_gamut_fine_mode) ?
		  MDP_GAMUT_TABLE_V1_7_SZ : MDP_GAMUT_TABLE_V1_7_COARSE_SZ;
	if (!(gamut_cfg_data->flags & MDP_PP_OPS_WRITE))
		goto bail_out;
	/* Sanity check for all tables */
	for (i = 0; i < MDP_GAMUT_TABLE_NUM_V1_7; i++) {
		if (!gamut_data->c0_data[i] || !gamut_data->c1_c2_data[i]
		    || (gamut_data->tbl_size[i] != tbl_sz)) {
			pr_err("invalid param for c0 %pK c1c2 %pK table %d size %d expected sz %d\n",
			       gamut_data->c0_data[i],
			       gamut_data->c1_c2_data[i], i,
			       gamut_data->tbl_size[i], tbl_sz);
			ret = -EINVAL;
			goto bail_out;
		}
		if (i < MDP_GAMUT_SCALE_OFF_TABLE_NUM &&
		    (!gamut_data->scale_off_data[i] ||
		    (gamut_data->tbl_scale_off_sz[i] !=
		    MDP_GAMUT_SCALE_OFF_SZ))) {
			pr_err("invalid param for scale table %pK for c%d size %d expected size%d\n",
				gamut_data->scale_off_data[i], i,
				gamut_data->tbl_scale_off_sz[i],
				MDP_GAMUT_SCALE_OFF_SZ);
			ret = -EINVAL;
			goto bail_out;
		}
	}
	base_addr_scale += GAMUT_C0_SCALE_OFF;
	writel_relaxed(GAMUT_CLK_GATING_INACTIVE, base_addr + GAMUT_CLK_CTRL);
	for (i = 0; i < MDP_GAMUT_TABLE_NUM_V1_7; i++) {
		val = index_start;
		val |= GAMUT_TABLE_SELECT(i);
		writel_relaxed(val, (base_addr + GAMUT_TABLE_INDEX));

		writel_relaxed(gamut_data->c1_c2_data[i][0],
				base_addr + GAMUT_TABLE_LOWER_GB);
		for (j = 0; j < gamut_data->tbl_size[i] - 1; j++) {
			gamut_val = gamut_data->c1_c2_data[i][j + 1];
			gamut_val = (gamut_val << 32) |
					gamut_data->c0_data[i][j];
			writeq_relaxed(gamut_val,
					base_addr + GAMUT_TABLE_UPPER_R);
		}
		writel_relaxed(gamut_data->c0_data[i][j],
				base_addr + GAMUT_TABLE_UPPER_R);
		if ((i >= MDP_GAMUT_SCALE_OFF_TABLE_NUM) ||
				(!gamut_data->map_en))
			continue;
		for (j = 0; j < MDP_GAMUT_SCALE_OFF_SZ; j++) {
			writel_relaxed((gamut_data->scale_off_data[i][j]),
				       base_addr_scale);
			base_addr_scale += 4;
		}
	}
	writel_relaxed(GAMUT_CLK_GATING_PARTIAL_ACTIVE,
				   base_addr + GAMUT_CLK_CTRL);
bail_out:
	if (!ret) {
		val = 0;
		pp_sts_set_split_bits(&pp_sts->gamut_sts,
				gamut_cfg_data->flags);
		if (gamut_cfg_data->flags & MDP_PP_OPS_DISABLE) {
			pp_sts->gamut_sts &= ~PP_STS_ENABLE;
			writel_relaxed(val, base_addr + GAMUT_OP_MODE_OFF);
		} else if (gamut_cfg_data->flags & MDP_PP_OPS_ENABLE) {
			pp_sts->gamut_sts |= PP_STS_ENABLE;
			if (pp_sts_is_enabled(pp_sts->gamut_sts,
					      pp_sts->side_sts)) {
				if (gamut_data->mode == mdp_gamut_coarse_mode)
					val |= GAMUT_COARSE_EN;
				if (gamut_data->map_en)
					val |= GAMUT_MAP_EN;
				val |= GAMUT_ENABLE;
			}
			writel_relaxed(val, base_addr + GAMUT_OP_MODE_OFF);
		}
	}
	return ret;
}

static int pp_pcc_set_config(char __iomem *base_addr,
			struct pp_sts_type *pp_sts, void *cfg_data,
			u32 block_type)
{
	struct mdp_pcc_cfg_data *pcc_cfg_data = NULL;
	struct mdp_pcc_data_v1_7 *pcc_data = NULL;
	char __iomem *addr = NULL;
	u32 opmode = 0;

	if (!base_addr || !cfg_data || !pp_sts) {
		pr_err("invalid params base_addr %pK cfg_data %pK pp_sts %pK\n",
			base_addr, cfg_data, pp_sts);
		return -EINVAL;
	}
	pcc_cfg_data = (struct mdp_pcc_cfg_data *) cfg_data;
	if (pcc_cfg_data->version != mdp_pcc_v1_7) {
		pr_err("invalid pcc version %d\n", pcc_cfg_data->version);
		return -EINVAL;
	}
	if (!(pcc_cfg_data->ops & ~(MDP_PP_OPS_READ))) {
		pr_info("only read ops is set %d\n", pcc_cfg_data->ops);
		return 0;
	}
	pcc_data = pcc_cfg_data->cfg_payload;
	if (!pcc_data) {
		pr_err("invalid payload for pcc %pK\n", pcc_data);
		return -EINVAL;
	}

	if (!(pcc_cfg_data->ops & MDP_PP_OPS_WRITE))
		goto bail_out;

	addr = base_addr + PCC_CONST_COEFF_OFF;
	writel_relaxed(pcc_data->r.c & PCC_CONST_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.c & PCC_CONST_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.c & PCC_CONST_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_R_COEFF_OFF;
	writel_relaxed(pcc_data->r.r & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.r & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.r & PCC_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_G_COEFF_OFF;
	writel_relaxed(pcc_data->r.g & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.g & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.g & PCC_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_B_COEFF_OFF;
	writel_relaxed(pcc_data->r.b & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.b & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.b & PCC_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_RG_COEFF_OFF;
	writel_relaxed(pcc_data->r.rg & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.rg & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.rg & PCC_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_RB_COEFF_OFF;
	writel_relaxed(pcc_data->r.rb & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.rb & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.rb & PCC_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_GB_COEFF_OFF;
	writel_relaxed(pcc_data->r.gb & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.gb & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.gb & PCC_COEFF_MASK, addr + 8);

	addr = base_addr + PCC_RGB_COEFF_OFF;
	writel_relaxed(pcc_data->r.rgb & PCC_COEFF_MASK, addr);
	writel_relaxed(pcc_data->g.rgb & PCC_COEFF_MASK, addr + 4);
	writel_relaxed(pcc_data->b.rgb & PCC_COEFF_MASK, addr + 8);

bail_out:
	pp_sts_set_split_bits(&pp_sts->pcc_sts, pcc_cfg_data->ops);
	if (pcc_cfg_data->ops & MDP_PP_OPS_DISABLE) {
		writel_relaxed(opmode, base_addr + PCC_OP_MODE_OFF);
		pp_sts->pcc_sts &= ~PP_STS_ENABLE;
	} else if (pcc_cfg_data->ops & MDP_PP_OPS_ENABLE) {
		pp_sts->pcc_sts |= PP_STS_ENABLE;
		if (pp_sts_is_enabled(pp_sts->pcc_sts, pp_sts->side_sts))
			opmode |= PCC_ENABLE;
		writel_relaxed(opmode, base_addr + PCC_OP_MODE_OFF);
	}

	return 0;
}

static int pp_pcc_get_config(char __iomem *base_addr, void *cfg_data,
				u32 block_type, u32 disp_num)
{
	char __iomem *addr;
	struct mdp_pcc_cfg_data *pcc_cfg = NULL;
	struct mdp_pcc_data_v1_7 pcc_data;

	if (!base_addr || !cfg_data) {
		pr_err("invalid params base_addr %pK cfg_data %pK\n",
		       base_addr, cfg_data);
		return -EINVAL;
	}

	pcc_cfg = (struct mdp_pcc_cfg_data *) cfg_data;
	if (pcc_cfg->version != mdp_pcc_v1_7) {
		pr_err("unsupported version of pcc %d\n",
		       pcc_cfg->version);
		return -EINVAL;
	}

	addr = base_addr + PCC_CONST_COEFF_OFF;
	pcc_data.r.c = readl_relaxed(addr) & PCC_CONST_COEFF_MASK;
	pcc_data.g.c = readl_relaxed(addr + 4) & PCC_CONST_COEFF_MASK;
	pcc_data.b.c = readl_relaxed(addr + 8) & PCC_CONST_COEFF_MASK;

	addr = base_addr + PCC_R_COEFF_OFF;
	pcc_data.r.r = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.r = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.r = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	addr = base_addr + PCC_G_COEFF_OFF;
	pcc_data.r.g = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.g = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.g = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	addr = base_addr + PCC_B_COEFF_OFF;
	pcc_data.r.b = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.b = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.b = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	addr = base_addr + PCC_RG_COEFF_OFF;
	pcc_data.r.rg = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.rg = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.rg = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	addr = base_addr + PCC_RB_COEFF_OFF;
	pcc_data.r.rb = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.rb = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.rb = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	addr = base_addr + PCC_GB_COEFF_OFF;
	pcc_data.r.gb = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.gb = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.gb = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	addr = base_addr + PCC_RGB_COEFF_OFF;
	pcc_data.r.rgb = readl_relaxed(addr) & PCC_COEFF_MASK;
	pcc_data.g.rgb = readl_relaxed(addr + 4) & PCC_COEFF_MASK;
	pcc_data.b.rgb = readl_relaxed(addr + 8) & PCC_COEFF_MASK;

	if (copy_to_user(pcc_cfg->cfg_payload, &pcc_data,
			 sizeof(pcc_data))) {
		pr_err("failed to copy the pcc info into payload\n");
		return -EFAULT;
	}

	return 0;
}

static void pp_pa_set_global_adj_regs(char __iomem *base_addr,
				struct mdp_pa_data_v1_7 *pa_data, u32 flags,
				int block_type)
{
	char __iomem *addr = NULL;

	if (block_type == DSPP)
		addr = base_addr + PA_DSPP_GLOBAL_OFF;
	else
		addr = base_addr + PA_VIG_GLOBAL_OFF;

	if (flags & MDP_PP_PA_HUE_ENABLE)
		writel_relaxed((pa_data->global_hue_adj &
				PA_GLOBAL_HUE_MASK), addr);
	addr += 4;
	if (flags & MDP_PP_PA_SAT_ENABLE)
		writel_relaxed((pa_data->global_sat_adj &
				PA_GLOBAL_SAT_MASK), addr);
	addr += 4;
	if (flags & MDP_PP_PA_VAL_ENABLE)
		writel_relaxed((pa_data->global_val_adj &
				PA_GLOBAL_VAL_MASK), addr);
	addr += 4;
	if (flags & MDP_PP_PA_CONT_ENABLE)
		writel_relaxed((pa_data->global_cont_adj &
				PA_GLOBAL_CONT_MASK), addr);
}

static void pp_pa_set_mem_col_regs(char __iomem *mem_col_p0_addr,
				char __iomem *mem_col_p2_addr,
				struct mdp_pa_mem_col_data_v1_7 *mem_col_data)
{
	writel_relaxed((mem_col_data->color_adjust_p0 &
			PA_MEM_COL_ADJ_P0_MASK), mem_col_p0_addr);
	mem_col_p0_addr += 4;
	writel_relaxed(mem_col_data->color_adjust_p1, mem_col_p0_addr);
	mem_col_p0_addr += 4;
	writel_relaxed((mem_col_data->hue_region &
			PA_MEM_COL_HUE_REGION_MASK), mem_col_p0_addr);
	mem_col_p0_addr += 4;
	writel_relaxed((mem_col_data->sat_region &
			PA_MEM_COL_SAT_REGION_MASK), mem_col_p0_addr);
	mem_col_p0_addr += 4;
	writel_relaxed((mem_col_data->val_region &
			PA_MEM_COL_VAL_REGION_MASK), mem_col_p0_addr);

	writel_relaxed(mem_col_data->color_adjust_p2, mem_col_p2_addr);
	mem_col_p2_addr += 4;
	writel_relaxed(mem_col_data->blend_gain, mem_col_p2_addr);
}

static void pp_pa_set_mem_col(char __iomem *base_addr,
				struct mdp_pa_data_v1_7 *pa_data, u32 flags,
				int block_type, uint32_t *pa_hold,
				uint32_t *pa_hold_mask)
{
	uint32_t sat_hold = 0, val_hold = 0, mem_col_hold = 0;
	u32 skin_p0_off = 0, skin_p2_off = 0;
	u32 sky_p0_off = 0, sky_p2_off = 0;
	u32 fol_p0_off = 0, fol_p2_off = 0;
	char __iomem *mem_col_p0_addr = NULL;
	char __iomem *mem_col_p2_addr = NULL;

	if (block_type == DSPP) {
		skin_p0_off = PA_DSPP_MEM_COL_SKIN_P0_OFF;
		skin_p2_off = PA_DSPP_MEM_COL_SKIN_P2_OFF;
		sky_p0_off = PA_DSPP_MEM_COL_SKY_P0_OFF;
		sky_p2_off = PA_DSPP_MEM_COL_SKY_P2_OFF;
		fol_p0_off = PA_DSPP_MEM_COL_FOL_P0_OFF;
		fol_p2_off = PA_DSPP_MEM_COL_FOL_P2_OFF;
	} else {
		skin_p0_off = PA_VIG_MEM_COL_SKIN_P0_OFF;
		skin_p2_off = PA_VIG_MEM_COL_SKIN_P2_OFF;
		sky_p0_off = PA_VIG_MEM_COL_SKY_P0_OFF;
		sky_p2_off = PA_VIG_MEM_COL_SKY_P2_OFF;
		fol_p0_off = PA_VIG_MEM_COL_FOL_P0_OFF;
		fol_p2_off = PA_VIG_MEM_COL_FOL_P2_OFF;
	}
	/* Update skin zone memory color registers */
	if (flags & MDP_PP_PA_SKIN_ENABLE) {
		mem_col_p0_addr = base_addr + skin_p0_off;
		mem_col_p2_addr = base_addr + skin_p2_off;
		pp_pa_set_mem_col_regs(mem_col_p0_addr, mem_col_p2_addr,
				       &pa_data->skin_cfg);
		sat_hold = (pa_data->skin_cfg.sat_hold & PA_HOLD_MASK) <<
			    PA_HOLD_SAT_SHIFT;
		val_hold = (pa_data->skin_cfg.val_hold & PA_HOLD_MASK) <<
			    PA_HOLD_VAL_SHIFT;
		mem_col_hold = (sat_hold | val_hold) << PA_HOLD_SKIN_SHIFT;
		*pa_hold |= mem_col_hold;
		*pa_hold_mask |= PA_HOLD_SKIN_MASK;
	}
	/* Update sky zone memory color registers */
	if (flags & MDP_PP_PA_SKY_ENABLE) {
		mem_col_p0_addr = base_addr + sky_p0_off;
		mem_col_p2_addr = base_addr + sky_p2_off;
		pp_pa_set_mem_col_regs(mem_col_p0_addr, mem_col_p2_addr,
				       &pa_data->sky_cfg);
		sat_hold = (pa_data->sky_cfg.sat_hold & PA_HOLD_MASK) <<
			    PA_HOLD_SAT_SHIFT;
		val_hold = (pa_data->sky_cfg.val_hold & PA_HOLD_MASK) <<
			    PA_HOLD_VAL_SHIFT;
		mem_col_hold = (sat_hold | val_hold) << PA_HOLD_SKY_SHIFT;
		*pa_hold |= mem_col_hold;
		*pa_hold_mask |= PA_HOLD_SKY_MASK;
	}
	/* Update foliage zone memory color registers */
	if (flags & MDP_PP_PA_FOL_ENABLE) {
		mem_col_p0_addr = base_addr + fol_p0_off;
		mem_col_p2_addr = base_addr + fol_p2_off;
		pp_pa_set_mem_col_regs(mem_col_p0_addr, mem_col_p2_addr,
				       &pa_data->fol_cfg);
		sat_hold = (pa_data->fol_cfg.sat_hold & PA_HOLD_MASK) <<
			    PA_HOLD_SAT_SHIFT;
		val_hold = (pa_data->fol_cfg.val_hold & PA_HOLD_MASK) <<
			    PA_HOLD_VAL_SHIFT;
		mem_col_hold = (sat_hold | val_hold) << PA_HOLD_FOL_SHIFT;
		*pa_hold |= mem_col_hold;
		*pa_hold_mask |= PA_HOLD_FOL_MASK;
	}
}

static void pp_pa_set_six_zone(char __iomem *base_addr,
				struct mdp_pa_data_v1_7 *pa_data,
				u32 flags,
				uint32_t *pa_hold,
				uint32_t *pa_hold_mask)
{
	u32 data, i;
	char __iomem *addr = base_addr + PA_SIX_ZONE_LUT_OFF;
	uint32_t sat_hold = 0, val_hold = 0, mem_col_hold = 0;
	/* Update six zone memory color registers */
	if (!(flags & MDP_PP_PA_SIX_ZONE_ENABLE))
		return;

	if (!pa_data->six_zone_len || !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;
	}

	writel_relaxed((pa_data->six_zone_curve_p1[0] &
			PA_SIX_ZONE_CURVE_P1_MASK), addr + 4);
	/* Index Update to trigger auto-incrementing LUT accesses */
	data = PA_SIX_ZONE_INDEX_UPDATE;
	writel_relaxed((pa_data->six_zone_curve_p0[0] &
			PA_SIX_ZONE_CURVE_P0_MASK) | data, addr);

	/* Remove Index Update */
	for (i = 1; i < MDP_SIX_ZONE_LUT_SIZE; i++) {
		writel_relaxed((pa_data->six_zone_curve_p1[i] &
				PA_SIX_ZONE_CURVE_P1_MASK), addr + 4);
		writel_relaxed((pa_data->six_zone_curve_p0[i] &
				PA_SIX_ZONE_CURVE_P0_MASK), addr);
	}
	addr = base_addr + PA_SIX_ZONE_REGION_OFF;
	writel_relaxed(pa_data->six_zone_thresh, addr);

	addr = base_addr + PA_SIX_ZONE_ADJ_OFF;
	writel_relaxed((pa_data->six_zone_adj_p0 &
			PA_SIX_ZONE_ADJ_P0_MASK), addr);
	addr += 4;
	writel_relaxed(pa_data->six_zone_adj_p1, addr);

	sat_hold = (pa_data->six_zone_sat_hold & PA_HOLD_MASK) <<
		    PA_HOLD_SAT_SHIFT;
	val_hold = (pa_data->six_zone_val_hold & PA_HOLD_MASK) <<
		    PA_HOLD_VAL_SHIFT;
	mem_col_hold = (sat_hold | val_hold) << PA_HOLD_SIX_ZONE_SHIFT;
	*pa_hold |= mem_col_hold;
	*pa_hold_mask |= PA_HOLD_SIX_ZONE_MASK;
}

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;
	uint32_t pa_hold = 0, pa_hold_mask = 0, pa_hold_tmp;
	char __iomem *pa_hold_addr = NULL;
	int ret = 0;

	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\n", pa_cfg_data->flags);
		return 0;
	}
	if (pa_cfg_data->flags & MDP_PP_OPS_DISABLE) {
		pr_debug("Disable PA\n");
		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;
	}

	if (!(pa_cfg_data->flags & MDP_PP_OPS_WRITE)) {
		pr_warn("No write flag enabled for PA flags %d\n",
			pa_cfg_data->flags);
		return 0;
	}

	pp_pa_set_global_adj_regs(base_addr, pa_data, pa_cfg_data->flags,
			block_type);
	pp_pa_set_mem_col(base_addr, pa_data, pa_cfg_data->flags,
			block_type, &pa_hold, &pa_hold_mask);
	if (block_type == DSPP)
		pp_pa_set_six_zone(base_addr, pa_data, pa_cfg_data->flags,
				   &pa_hold, &pa_hold_mask);

	/*
	 * Only modify the PA hold bits for PA features that have
	 * been updated.
	 */
	if (block_type == DSPP)
		pa_hold_addr = base_addr + PA_DSPP_HOLD_OFF;
	else
		pa_hold_addr = base_addr + PA_VIG_HOLD_OFF;
	pa_hold_tmp = readl_relaxed(pa_hold_addr);
	pa_hold_tmp &= ~pa_hold_mask;
	pa_hold |= pa_hold_tmp;
	writel_relaxed(pa_hold, pa_hold_addr);

pa_set_sts:
	pp_pa_set_sts(pp_sts, pa_data, pa_cfg_data->flags, block_type);

	return ret;
}

static void pp_pa_get_global_adj_regs(char __iomem *base_addr,
				struct mdp_pa_data_v1_7 *pa_data, u32 flags,
				int block_type)
{
	char __iomem *addr = NULL;

	if (block_type == DSPP)
		addr = base_addr + PA_DSPP_GLOBAL_OFF;
	else
		addr = base_addr + PA_VIG_GLOBAL_OFF;

	if (flags & MDP_PP_PA_HUE_ENABLE)
		pa_data->global_hue_adj = readl_relaxed(addr) &
					  PA_GLOBAL_HUE_MASK;
	addr += 4;
	if (flags & MDP_PP_PA_SAT_ENABLE)
		pa_data->global_sat_adj = readl_relaxed(addr) &
					  PA_GLOBAL_SAT_MASK;
	addr += 4;
	if (flags & MDP_PP_PA_VAL_ENABLE)
		pa_data->global_val_adj = readl_relaxed(addr) &
					  PA_GLOBAL_VAL_MASK;
	addr += 4;
	if (flags & MDP_PP_PA_CONT_ENABLE)
		pa_data->global_cont_adj = readl_relaxed(addr) &
					  PA_GLOBAL_CONT_MASK;
}

static void pp_pa_get_mem_col_regs(char __iomem *mem_col_p0_addr,
				char __iomem *mem_col_p2_addr,
				struct mdp_pa_mem_col_data_v1_7 *mem_col_data)
{
	mem_col_data->color_adjust_p0 = readl_relaxed(mem_col_p0_addr) &
					PA_MEM_COL_ADJ_P0_MASK;
	mem_col_p0_addr += 4;
	mem_col_data->color_adjust_p1 = readl_relaxed(mem_col_p0_addr);
	mem_col_p0_addr += 4;
	mem_col_data->hue_region = readl_relaxed(mem_col_p0_addr) &
				   PA_MEM_COL_HUE_REGION_MASK;
	mem_col_p0_addr += 4;
	mem_col_data->sat_region = readl_relaxed(mem_col_p0_addr) &
				   PA_MEM_COL_SAT_REGION_MASK;
	mem_col_p0_addr += 4;
	mem_col_data->val_region = readl_relaxed(mem_col_p0_addr) &
				   PA_MEM_COL_VAL_REGION_MASK;

	mem_col_data->color_adjust_p2 = readl_relaxed(mem_col_p2_addr);
	mem_col_p2_addr += 4;
	mem_col_data->blend_gain = readl_relaxed(mem_col_p2_addr);
}

static void pp_pa_get_mem_col(char __iomem *base_addr,
				struct mdp_pa_data_v1_7 *pa_data, u32 flags,
				int block_type,
				uint32_t pa_hold)
{
	uint32_t mem_col_hold = 0;
	u32 skin_p0_off = 0, skin_p2_off = 0;
	u32 sky_p0_off = 0, sky_p2_off = 0;
	u32 fol_p0_off = 0, fol_p2_off = 0;
	char __iomem *mem_col_p0_addr = NULL;
	char __iomem *mem_col_p2_addr = NULL;

	if (block_type == DSPP) {
		skin_p0_off = PA_DSPP_MEM_COL_SKIN_P0_OFF;
		skin_p2_off = PA_DSPP_MEM_COL_SKIN_P2_OFF;
		sky_p0_off = PA_DSPP_MEM_COL_SKY_P0_OFF;
		sky_p2_off = PA_DSPP_MEM_COL_SKY_P2_OFF;
		fol_p0_off = PA_DSPP_MEM_COL_FOL_P0_OFF;
		fol_p2_off = PA_DSPP_MEM_COL_FOL_P2_OFF;
	} else {
		skin_p0_off = PA_VIG_MEM_COL_SKIN_P0_OFF;
		skin_p2_off = PA_VIG_MEM_COL_SKIN_P2_OFF;
		sky_p0_off = PA_VIG_MEM_COL_SKY_P0_OFF;
		sky_p2_off = PA_VIG_MEM_COL_SKY_P2_OFF;
		fol_p0_off = PA_VIG_MEM_COL_FOL_P0_OFF;
		fol_p2_off = PA_VIG_MEM_COL_FOL_P2_OFF;
	}
	/* Update skin zone memory color registers */
	if (flags & MDP_PP_PA_SKIN_ENABLE) {
		mem_col_p0_addr = base_addr + skin_p0_off;
		mem_col_p2_addr = base_addr + skin_p2_off;
		pp_pa_get_mem_col_regs(mem_col_p0_addr, mem_col_p2_addr,
				       &pa_data->skin_cfg);
		mem_col_hold = pa_hold >> PA_HOLD_SKIN_SHIFT;
		pa_data->skin_cfg.sat_hold = (mem_col_hold >>
				PA_HOLD_SAT_SHIFT) & PA_HOLD_MASK;
		pa_data->skin_cfg.val_hold = (mem_col_hold >>
				PA_HOLD_VAL_SHIFT) & PA_HOLD_MASK;
	}
	/* Update sky zone memory color registers */
	if (flags & MDP_PP_PA_SKY_ENABLE) {
		mem_col_p0_addr = base_addr + sky_p0_off;
		mem_col_p2_addr = base_addr + sky_p2_off;
		pp_pa_get_mem_col_regs(mem_col_p0_addr, mem_col_p2_addr,
				       &pa_data->sky_cfg);
		mem_col_hold = pa_hold >> PA_HOLD_SKY_SHIFT;
		pa_data->sky_cfg.sat_hold = (mem_col_hold >>
				PA_HOLD_SAT_SHIFT) & PA_HOLD_MASK;
		pa_data->sky_cfg.val_hold = (mem_col_hold >>
				PA_HOLD_VAL_SHIFT) & PA_HOLD_MASK;
	}
	/* Update foliage zone memory color registers */
	if (flags & MDP_PP_PA_FOL_ENABLE) {
		mem_col_p0_addr = base_addr + fol_p0_off;
		mem_col_p2_addr = base_addr + fol_p2_off;
		pp_pa_get_mem_col_regs(mem_col_p0_addr, mem_col_p2_addr,
				       &pa_data->fol_cfg);
		mem_col_hold = pa_hold >> PA_HOLD_FOL_SHIFT;
		pa_data->fol_cfg.sat_hold = (mem_col_hold >>
				PA_HOLD_SAT_SHIFT) & PA_HOLD_MASK;
		pa_data->fol_cfg.val_hold = (mem_col_hold >>
				PA_HOLD_VAL_SHIFT) & PA_HOLD_MASK;
	}
}

static int pp_pa_get_six_zone(char __iomem *base_addr,
				struct mdp_pa_data_v1_7 *pa_data, u32 flags,
				u32 pa_hold)
{
	uint32_t six_zone_sz = 0, six_zone_buf_sz = 0;
	u32 data = 0;
	char __iomem *addr = base_addr + PA_SIX_ZONE_LUT_OFF;
	uint32_t *six_zone_read_buf = NULL;
	uint32_t *six_zone_p0 = NULL, *six_zone_p1 = NULL;
	uint32_t six_zone_hold = 0;
	int ret = 0, i;

	if (pa_data->six_zone_len != MDP_SIX_ZONE_LUT_SIZE) {
		pr_err("Invalid six zone length %d\n",
			pa_data->six_zone_len);
		return -EINVAL;
	}
	six_zone_sz = pa_data->six_zone_len * sizeof(uint32_t);

	if (!access_ok(VERIFY_WRITE, pa_data->six_zone_curve_p0,
			six_zone_sz)) {
		pr_err("invalid six_zone_curve_p0 addr for sz %d\n",
			six_zone_sz);
		return -EFAULT;
	}
	if (!access_ok(VERIFY_WRITE, pa_data->six_zone_curve_p1,
			six_zone_sz)) {
		pr_err("invalid six_zone_curve_p1 addr for sz %d\n",
			six_zone_sz);
		return -EFAULT;
	}

	six_zone_buf_sz = 2 * six_zone_sz;
	six_zone_read_buf = kzalloc(six_zone_buf_sz, GFP_KERNEL);
	if (!six_zone_read_buf) {
		pr_err("allocation failed for six zone lut size %d\n",
			six_zone_buf_sz);
		ret = -ENOMEM;
		goto six_zone_exit;
	}
	six_zone_p0 = six_zone_read_buf;
	six_zone_p1 = &six_zone_read_buf[MDP_SIX_ZONE_LUT_SIZE];

	data = PA_SIX_ZONE_VALUE_UPDATE | PA_SIX_ZONE_INDEX_UPDATE;
	writel_relaxed(data, addr);

	for (i = 0; i < MDP_SIX_ZONE_LUT_SIZE; i++) {
		six_zone_p1[i] = readl_relaxed(addr + 4) &
				 PA_SIX_ZONE_CURVE_P1_MASK;
		six_zone_p0[i] = readl_relaxed(addr) &
				 PA_SIX_ZONE_CURVE_P0_MASK;
	}

	addr = base_addr + PA_SIX_ZONE_REGION_OFF;
	pa_data->six_zone_thresh = readl_relaxed(addr);

	addr = base_addr + PA_SIX_ZONE_ADJ_OFF;
	pa_data->six_zone_adj_p0 = readl_relaxed(addr) &
				   PA_SIX_ZONE_ADJ_P0_MASK;
	addr += 4;
	pa_data->six_zone_adj_p1 = readl_relaxed(addr);

	if (copy_to_user(pa_data->six_zone_curve_p0, six_zone_p0,
			 six_zone_sz)) {
		pr_err("Failed to copy six zone p0 data\n");
		ret = -EFAULT;
		goto six_zone_memory_exit;
	}
	if (copy_to_user(pa_data->six_zone_curve_p1, six_zone_p1,
			 six_zone_sz)) {
		pr_err("Failed to copy six zone p1 data\n");
		ret = -EFAULT;
		goto six_zone_memory_exit;
	}

	six_zone_hold = pa_hold >> PA_HOLD_SIX_ZONE_SHIFT;
	pa_data->six_zone_sat_hold = (six_zone_hold >> PA_HOLD_SAT_SHIFT) &
				     PA_HOLD_MASK;
	pa_data->six_zone_val_hold = (six_zone_hold >> PA_HOLD_VAL_SHIFT) &
				     PA_HOLD_MASK;

six_zone_memory_exit:
	kfree(six_zone_read_buf);
six_zone_exit:
	return ret;
}

static int pp_pa_get_config(char __iomem *base_addr, void *cfg_data,
				u32 block_type, u32 disp_num)
{
	struct mdp_pa_v2_cfg_data *pa_cfg_data = NULL;
	struct mdp_pa_data_v1_7 pa_data;
	int ret = 0;
	uint32_t pa_hold = 0;
	char __iomem *pa_hold_addr = 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) && (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 (copy_from_user(&pa_data, pa_cfg_data->cfg_payload,
			sizeof(pa_data))) {
		pr_err("copy from user failed for pa data\n");
		return -EFAULT;
	}

	if (block_type == DSPP)
		pa_hold_addr = base_addr + PA_DSPP_HOLD_OFF;
	else
		pa_hold_addr = base_addr + PA_VIG_HOLD_OFF;
	pa_hold = readl_relaxed(pa_hold_addr);

	if ((block_type == DSPP) &&
	    (pa_cfg_data->flags & MDP_PP_PA_SIX_ZONE_ENABLE)) {
		ret = pp_pa_get_six_zone(base_addr,
				   &pa_data,
				   pa_cfg_data->flags,
				   pa_hold);
		if (ret) {
			pr_err("six zone read failed ret %d\n", ret);
			return ret;
		}
	}
	pp_pa_get_global_adj_regs(base_addr, &pa_data, pa_cfg_data->flags,
			block_type);
	pp_pa_get_mem_col(base_addr, &pa_data, pa_cfg_data->flags,
			block_type, pa_hold);

	ret = copy_to_user(pa_cfg_data->cfg_payload, &pa_data, sizeof(pa_data));
	if (ret) {
		pr_err("Failed to copy PA data to user\n");
		return -EFAULT;
	}

	return 0;
}

static void pp_pa_update_vig_opmode(int pa_sts, u32 *opmode)
{
	*opmode |= PA_VIG_OP_ENABLE;
	if (pa_sts & PP_STS_PA_HUE_MASK)
		*opmode |= PA_VIG_OP_HUE_MASK;
	if (pa_sts & PP_STS_PA_SAT_MASK)
		*opmode |= PA_VIG_OP_SAT_MASK;
	if (pa_sts & PP_STS_PA_VAL_MASK)
		*opmode |= PA_VIG_OP_VAL_MASK;
	if (pa_sts & PP_STS_PA_CONT_MASK)
		*opmode |= PA_VIG_OP_CONT_MASK;
	if (pa_sts & PP_STS_PA_SAT_ZERO_EXP_EN)
		*opmode |= PA_VIG_OP_SAT_ZERO_EXP_EN;
	if (pa_sts & PP_STS_PA_MEM_COL_SKIN_MASK)
		*opmode |= PA_VIG_OP_MEM_COL_SKIN_MASK;
	if (pa_sts & PP_STS_PA_MEM_COL_FOL_MASK)
		*opmode |= PA_VIG_OP_MEM_COL_FOL_MASK;
	if (pa_sts & PP_STS_PA_MEM_COL_SKY_MASK)
		*opmode |= PA_VIG_OP_MEM_COL_SKY_MASK;
	if (pa_sts & PP_STS_PA_MEM_PROT_HUE_EN)
		*opmode |= PA_VIG_OP_MEM_PROT_HUE_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_SAT_EN)
		*opmode |= PA_VIG_OP_MEM_PROT_SAT_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_VAL_EN)
		*opmode |= PA_VIG_OP_MEM_PROT_VAL_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_CONT_EN)
		*opmode |= PA_VIG_OP_MEM_PROT_CONT_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_BLEND_EN)
		*opmode |= PA_VIG_OP_MEM_PROT_BLEND_EN;
}

static void pp_pa_update_dspp_opmode(int pa_sts, u32 *opmode)
{
	*opmode |= PA_DSPP_OP_ENABLE;
	if (pa_sts & PP_STS_PA_HUE_MASK)
		*opmode |= PA_DSPP_OP_HUE_MASK;
	if (pa_sts & PP_STS_PA_SAT_MASK)
		*opmode |= PA_DSPP_OP_SAT_MASK;
	if (pa_sts & PP_STS_PA_VAL_MASK)
		*opmode |= PA_DSPP_OP_VAL_MASK;
	if (pa_sts & PP_STS_PA_CONT_MASK)
		*opmode |= PA_DSPP_OP_CONT_MASK;
	if (pa_sts & PP_STS_PA_SAT_ZERO_EXP_EN)
		*opmode |= PA_DSPP_OP_SAT_ZERO_EXP_EN;
	if (pa_sts & PP_STS_PA_MEM_COL_SKIN_MASK)
		*opmode |= PA_DSPP_OP_MEM_COL_SKIN_MASK;
	if (pa_sts & PP_STS_PA_MEM_COL_FOL_MASK)
		*opmode |= PA_DSPP_OP_MEM_COL_FOL_MASK;
	if (pa_sts & PP_STS_PA_MEM_COL_SKY_MASK)
		*opmode |= PA_DSPP_OP_MEM_COL_SKY_MASK;
	if (pa_sts & PP_STS_PA_SIX_ZONE_HUE_MASK)
		*opmode |= PA_DSPP_OP_SIX_ZONE_HUE_MASK;
	if (pa_sts & PP_STS_PA_SIX_ZONE_SAT_MASK)
		*opmode |= PA_DSPP_OP_SIX_ZONE_SAT_MASK;
	if (pa_sts & PP_STS_PA_SIX_ZONE_VAL_MASK)
		*opmode |= PA_DSPP_OP_SIX_ZONE_VAL_MASK;
	if (pa_sts & PP_STS_PA_MEM_PROT_HUE_EN)
		*opmode |= PA_DSPP_OP_MEM_PROT_HUE_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_SAT_EN)
		*opmode |= PA_DSPP_OP_MEM_PROT_SAT_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_VAL_EN)
		*opmode |= PA_DSPP_OP_MEM_PROT_VAL_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_CONT_EN)
		*opmode |= PA_DSPP_OP_MEM_PROT_CONT_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_BLEND_EN)
		*opmode |= PA_DSPP_OP_MEM_PROT_BLEND_EN;
	if (pa_sts & PP_STS_PA_MEM_PROT_SIX_EN)
		*opmode |= PA_DSPP_OP_MEM_PROT_SIX_EN;
}

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 %pK cfg_data %pK pp_sts_type %pK\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_once("invalid igc version %d payload %pK\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\n", 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 %pK  c2_data %pK\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_v1_7;
	struct mdp_igc_lut_data_v1_7 *lut_data = &lut_data_v1_7;
	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 %pK cfg_data %pK 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 %pK block %d\n",
		       lut_cfg_data->version, lut_cfg_data->cfg_payload,
		       lut_cfg_data->block);
		ret = -EINVAL;
		goto exit;
	}
	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 != 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) {
		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\n");
		ret = -EFAULT;
	}
	if (!ret && copy_to_user(lut_data->c2_data, c2_data, sz)) {
		pr_err("failed to copy the c2 data\n");
		ret = -EFAULT;
	}
	kfree(c0c1_data);
exit:
	return ret;
}


static int pp_pgc_set_config(char __iomem *base_addr,
		struct pp_sts_type *pp_sts, void *cfg_data,
		u32 block_type)
{
	char __iomem *c0 = NULL, *c1 = NULL, *c2 = NULL;
	u32 val = 0, i = 0, *sts = NULL;
	struct mdp_pgc_lut_data *pgc_data = NULL;
	struct mdp_pgc_lut_data_v1_7  *pgc_data_v17 = 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 != LM) {
		pr_err("invalid block type %d\n", block_type);
		return -EINVAL;
	}
	sts = (block_type == DSPP) ? &pp_sts->pgc_sts : &pp_sts->argc_sts;
	pgc_data = (struct mdp_pgc_lut_data *) cfg_data;
	if (pgc_data->version != mdp_pgc_v1_7) {
		pr_err("invalid pgc version %d\n",
			pgc_data->version);
		return -EINVAL;
	}
	if (!(pgc_data->flags & ~(MDP_PP_OPS_READ))) {
		pr_debug("only read ops is set %d\n", pgc_data->flags);
		return 0;
	}
	if (pgc_data->flags & MDP_PP_OPS_DISABLE) {
		pr_debug("disable GC\n");
		goto set_ops;
	}

	pgc_data_v17 = (struct mdp_pgc_lut_data_v1_7 *) pgc_data->cfg_payload;
	if (!pgc_data_v17) {
		pr_err("invalid payload for GC %pK\n", pgc_data_v17);
		return -EINVAL;
	}

	if (pgc_data_v17->len != PGC_LUT_ENTRIES || !pgc_data_v17->c0_data ||
	    !pgc_data_v17->c1_data || !pgc_data_v17->c2_data) {
		pr_err("Invalid params entries %d c0_data %pK c1_data %pK c2_data %pK\n",
			pgc_data_v17->len, pgc_data_v17->c0_data,
			pgc_data_v17->c1_data, pgc_data_v17->c2_data);
		return -EINVAL;
	}
	c0 = base_addr + PGC_C0_LUT_INDEX;
	c1 = c0 + PGC_C1C2_LUT_OFF;
	c2 = c1 + PGC_C1C2_LUT_OFF;
	/*  set the indexes to zero */
	writel_relaxed(0, c0 + PGC_INDEX_OFF);
	writel_relaxed(0, c1 + PGC_INDEX_OFF);
	writel_relaxed(0, c2 + PGC_INDEX_OFF);
	for (i = 0; i < PGC_LUT_ENTRIES; i += 2) {
		val = pgc_data_v17->c0_data[i] & PGC_DATA_MASK;
		val |= (pgc_data_v17->c0_data[i + 1] & PGC_DATA_MASK) <<
			PGC_ODD_SHIFT;
		writel_relaxed(val, c0);
		val = pgc_data_v17->c1_data[i] & PGC_DATA_MASK;
		val |= (pgc_data_v17->c1_data[i + 1] & PGC_DATA_MASK) <<
			PGC_ODD_SHIFT;
		writel_relaxed(val, c1);
		val = pgc_data_v17->c2_data[i] & PGC_DATA_MASK;
		val |= (pgc_data_v17->c2_data[i + 1] & PGC_DATA_MASK) <<
			PGC_ODD_SHIFT;
		writel_relaxed(val, c2);
	}
	if (block_type == DSPP) {
		val = PGC_SWAP;
		writel_relaxed(val, base_addr + PGC_LUT_SWAP);
	}

set_ops:
	if (pgc_data->flags & MDP_PP_OPS_DISABLE) {
		*sts &= ~PP_STS_ENABLE;
		writel_relaxed(0, base_addr + PGC_OPMODE_OFF);
	} else if (pgc_data->flags & MDP_PP_OPS_ENABLE) {
		val = PGC_ENABLE;
		val |= (pgc_data->flags & MDP_PP_PGC_ROUNDING_ENABLE)
			? BIT(1) : 0;
		writel_relaxed(val, base_addr + PGC_OPMODE_OFF);
		*sts |= PP_STS_ENABLE;
	}
	return 0;
}

static int pp_pgc_get_config(char __iomem *base_addr, void *cfg_data,
			   u32 block_type, u32 disp_num)
{
	int ret = 0;
	char __iomem *c0 = NULL, *c1 = NULL, *c2 = NULL;
	u32 *c0_data = NULL, *c1_data = NULL, *c2_data = NULL;
	u32 val = 0, i = 0, sz = 0;
	struct mdp_pgc_lut_data *pgc_data = NULL;
	struct mdp_pgc_lut_data_v1_7  pgc_lut_data_v17;
	struct mdp_pgc_lut_data_v1_7  *pgc_data_v17 = &pgc_lut_data_v17;

	if (!base_addr || !cfg_data) {
		pr_err("invalid params base_addr %pK cfg_data %pK block_type %d\n",
		      base_addr, cfg_data, block_type);
		return -EINVAL;
	}
	pgc_data = (struct mdp_pgc_lut_data *) cfg_data;
	if (pgc_data->version != mdp_pgc_v1_7 || !pgc_data->cfg_payload) {
		pr_err("invalid pgc version %d payload %pK\n",
			pgc_data->version, pgc_data->cfg_payload);
		return -EINVAL;
	}
	if (copy_from_user(pgc_data_v17, (void __user *) pgc_data->cfg_payload,
			sizeof(*pgc_data_v17))) {
		pr_err("copy from user failed for pgc lut data\n");
		return -EFAULT;
	}
	if (!(pgc_data->flags & MDP_PP_OPS_READ)) {
		pr_info("read ops is not set %d\n", pgc_data->flags);
		return -EINVAL;
	}
	sz = PGC_LUT_ENTRIES * sizeof(u32);
	if (!access_ok(VERIFY_WRITE, pgc_data_v17->c0_data, sz) ||
	    !access_ok(VERIFY_WRITE, pgc_data_v17->c1_data, sz) ||
	    !access_ok(VERIFY_WRITE, pgc_data_v17->c2_data, sz)) {
		pr_err("incorrect payload for PGC read size %d\n",
			PGC_LUT_ENTRIES);
		return -EFAULT;
	}
	c0_data = kzalloc(sz * 3, GFP_KERNEL);
	if (!c0_data)
		return -ENOMEM;

	c1_data = c0_data + PGC_LUT_ENTRIES;
	c2_data = c1_data + PGC_LUT_ENTRIES;
	c0 = base_addr + PGC_C0_LUT_INDEX;
	c1 = c0 + PGC_C1C2_LUT_OFF;
	c2 = c1 + PGC_C1C2_LUT_OFF;
	/*  set the indexes to zero */
	writel_relaxed(0, c0 + 4);
	writel_relaxed(0, c1 + 4);
	writel_relaxed(0, c2 + 4);
	for (i = 0; i < PGC_LUT_ENTRIES; i += 2) {
		val = readl_relaxed(c0);
		c0_data[i] = val & PGC_DATA_MASK;
		c0_data[i + 1] = (val >> PGC_ODD_SHIFT) & PGC_DATA_MASK;
		val = readl_relaxed(c1);
		c1_data[i] = val & PGC_DATA_MASK;
		c1_data[i + 1] = (val >> PGC_ODD_SHIFT) & PGC_DATA_MASK;
		val = readl_relaxed(c2);
		c2_data[i] = val & PGC_DATA_MASK;
		c2_data[i + 1] = (val >> PGC_ODD_SHIFT) & PGC_DATA_MASK;
	}
	if (copy_to_user(pgc_data_v17->c0_data, c0_data, sz)) {
		pr_err("failed to copyuser c0 data of sz %d\n", sz);
		ret = -EFAULT;
	}
	if (!ret && copy_to_user(pgc_data_v17->c1_data, c1_data, sz)) {
		pr_err("failed to copyuser c1 data of sz %d\n", sz);
		ret = -EFAULT;
	}
	if (!ret && copy_to_user(pgc_data_v17->c2_data, c2_data, sz)) {
		pr_err("failed to copyuser c2 data of sz %d\n", sz);
		ret = -EFAULT;
	}
	if (!ret)
		pgc_data_v17->len = PGC_LUT_ENTRIES;
	kfree(c0_data);
	return ret;
}

static int pp_pcc_get_version(u32 *version)
{
	if (!version) {
		pr_err("invalid param version %pK\n", version);
		return -EINVAL;
	}
	*version = mdp_pcc_v1_7;
	return 0;
}

static int pp_igc_get_version(u32 *version)
{
	if (!version) {
		pr_err("invalid param version %pK\n", version);
		return -EINVAL;
	}
	*version = mdp_igc_v1_7;
	return 0;
}

static int pp_pgc_get_version(u32 *version)
{
	if (!version) {
		pr_err("invalid param version %pK\n", version);
		return -EINVAL;
	}
	*version = mdp_pgc_v1_7;
	return 0;
}

static int pp_pa_get_version(u32 *version)
{
	if (!version) {
		pr_err("invalid param version %pK\n", version);
		return -EINVAL;
	}
	*version = mdp_pa_v1_7;
	return 0;
}

static int pp_gamut_get_version(u32 *version)
{
	if (!version) {
		pr_err("invalid param version %pK\n", version);
		return -EINVAL;
	}
	*version = mdp_gamut_v1_7;
	return 0;
}

static int pp_dither_get_version(u32 *version)
{
	if (!version) {
		pr_err("invalid param version %pK\n", version);
		return -EINVAL;
	}
	*version = mdp_dither_v1_7;
	return 0;
}

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_gamut_clock_gating_en(char __iomem *base_addr)
{
	u32 val;

	if (base_addr) {
		val = readl_relaxed(base_addr + GAMUT_CLK_CTRL);
		if (val == GAMUT_CLK_GATING_PARTIAL_ACTIVE)
			writel_relaxed(GAMUT_CLK_GATING_ACTIVE,
						   base_addr + GAMUT_CLK_CTRL);
	}
}
