// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */
#include <drm/msm_drm_pp.h>
#include "sde_hw_catalog.h"
#include "sde_hw_util.h"
#include "sde_hw_mdss.h"
#include "sde_hw_lm.h"
#include "sde_ad4.h"

#define AD_STATE_READY(x) \
	(((x) & ad4_init) && \
	((x) & ad4_cfg) && \
	((x) & ad4_mode) && \
	(((x) & ad4_input) | ((x) & ad4_strength)))

#define MERGE_WIDTH_RIGHT 6
#define MERGE_WIDTH_LEFT 5
#define AD_IPC_FRAME_COUNT 2

enum ad4_ops_bitmask {
	ad4_init = BIT(AD_INIT),
	ad4_cfg = BIT(AD_CFG),
	ad4_mode = BIT(AD_MODE),
	ad4_input = BIT(AD_INPUT),
	ad4_strength = BIT(AD_STRENGTH),
	ad4_ops_max = BIT(31),
};

enum ad4_state {
	ad4_state_idle,
	ad4_state_startup,
	ad4_state_run,
	/* idle power collapse suspend state */
	ad4_state_ipcs,
	/* idle power collapse resume state */
	ad4_state_ipcr,
	/* manual mode state */
	ad4_state_manual,
	ad4_state_max,
};

struct ad4_roi_info {
	u32 h_start;
	u32 h_end;
	u32 v_start;
	u32 v_end;
	u32 f_in;
	u32 f_out;
};

typedef int (*ad4_prop_setup)(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *ad);

static int ad4_params_check(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);

static int ad4_no_op_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
static int ad4_setup_debug(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
static int ad4_setup_debug_manual(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_mode_setup(struct sde_hw_dspp *dspp, enum ad4_modes mode);
static int ad4_mode_setup_common(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_init_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
static int ad4_init_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_init_setup_run(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_init_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_cfg_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
static int ad4_cfg_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_cfg_setup_run(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_cfg_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_input_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_roi_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg);
static int ad4_roi_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_roi_coordinate_offset(struct sde_hw_cp_cfg *hw_cfg,
		struct ad4_roi_info *output);
static int ad4_input_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_input_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_suspend_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_assertive_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_assertive_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_backlight_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_backlight_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_strength_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_strength_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);

static int ad4_ipc_suspend_setup_run(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_ipc_suspend_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_ipc_resume_setup_ipcs(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_ipc_reset_setup_startup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_ipc_reset_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);
static int ad4_cfg_ipc_reset(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg);

static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
	[ad4_state_idle][AD_MODE] = ad4_mode_setup_common,
	[ad4_state_idle][AD_INIT] = ad4_init_setup_idle,
	[ad4_state_idle][AD_CFG] = ad4_cfg_setup_idle,
	[ad4_state_idle][AD_INPUT] = ad4_input_setup_idle,
	[ad4_state_idle][AD_SUSPEND] = ad4_suspend_setup,
	[ad4_state_idle][AD_ASSERTIVE] = ad4_assertive_setup,
	[ad4_state_idle][AD_BACKLIGHT] = ad4_backlight_setup,
	[ad4_state_idle][AD_STRENGTH] = ad4_strength_setup_idle,
	[ad4_state_idle][AD_ROI] = ad4_roi_setup,
	[ad4_state_idle][AD_IPC_SUSPEND] = ad4_no_op_setup,
	[ad4_state_idle][AD_IPC_RESUME] = ad4_no_op_setup,
	[ad4_state_idle][AD_IPC_RESET] = ad4_no_op_setup,

	[ad4_state_startup][AD_MODE] = ad4_mode_setup_common,
	[ad4_state_startup][AD_INIT] = ad4_init_setup,
	[ad4_state_startup][AD_CFG] = ad4_cfg_setup,
	[ad4_state_startup][AD_INPUT] = ad4_input_setup,
	[ad4_state_startup][AD_SUSPEND] = ad4_suspend_setup,
	[ad4_state_startup][AD_ASSERTIVE] = ad4_assertive_setup,
	[ad4_state_startup][AD_BACKLIGHT] = ad4_backlight_setup,
	[ad4_state_startup][AD_IPC_SUSPEND] = ad4_no_op_setup,
	[ad4_state_startup][AD_STRENGTH] = ad4_no_op_setup,
	[ad4_state_startup][AD_ROI] = ad4_roi_setup,
	[ad4_state_startup][AD_IPC_RESUME] = ad4_no_op_setup,
	[ad4_state_startup][AD_IPC_RESET] = ad4_ipc_reset_setup_startup,

	[ad4_state_run][AD_MODE] = ad4_mode_setup_common,
	[ad4_state_run][AD_INIT] = ad4_init_setup_run,
	[ad4_state_run][AD_CFG] = ad4_cfg_setup_run,
	[ad4_state_run][AD_INPUT] = ad4_input_setup,
	[ad4_state_run][AD_SUSPEND] = ad4_suspend_setup,
	[ad4_state_run][AD_ASSERTIVE] = ad4_assertive_setup,
	[ad4_state_run][AD_BACKLIGHT] = ad4_backlight_setup,
	[ad4_state_run][AD_STRENGTH] = ad4_no_op_setup,
	[ad4_state_run][AD_ROI] = ad4_roi_setup,
	[ad4_state_run][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_run,
	[ad4_state_run][AD_IPC_RESUME] = ad4_no_op_setup,
	[ad4_state_run][AD_IPC_RESET] = ad4_setup_debug,

	[ad4_state_ipcs][AD_MODE] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_INIT] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_CFG] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_INPUT] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_SUSPEND] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_ASSERTIVE] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_BACKLIGHT] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_STRENGTH] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_ROI] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_IPC_SUSPEND] = ad4_no_op_setup,
	[ad4_state_ipcs][AD_IPC_RESUME] = ad4_ipc_resume_setup_ipcs,
	[ad4_state_ipcs][AD_IPC_RESET] = ad4_no_op_setup,

	[ad4_state_ipcr][AD_MODE] = ad4_mode_setup_common,
	[ad4_state_ipcr][AD_INIT] = ad4_init_setup_ipcr,
	[ad4_state_ipcr][AD_CFG] = ad4_cfg_setup_ipcr,
	[ad4_state_ipcr][AD_INPUT] = ad4_input_setup_ipcr,
	[ad4_state_ipcr][AD_SUSPEND] = ad4_suspend_setup,
	[ad4_state_ipcr][AD_ASSERTIVE] = ad4_assertive_setup_ipcr,
	[ad4_state_ipcr][AD_BACKLIGHT] = ad4_backlight_setup_ipcr,
	[ad4_state_ipcr][AD_STRENGTH] = ad4_no_op_setup,
	[ad4_state_ipcr][AD_ROI] = ad4_roi_setup_ipcr,
	[ad4_state_ipcr][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_ipcr,
	[ad4_state_ipcr][AD_IPC_RESUME] = ad4_no_op_setup,
	[ad4_state_ipcr][AD_IPC_RESET] = ad4_ipc_reset_setup_ipcr,

	[ad4_state_manual][AD_MODE] = ad4_mode_setup_common,
	[ad4_state_manual][AD_INIT] = ad4_init_setup,
	[ad4_state_manual][AD_CFG] = ad4_cfg_setup,
	[ad4_state_manual][AD_INPUT] = ad4_no_op_setup,
	[ad4_state_manual][AD_SUSPEND] = ad4_no_op_setup,
	[ad4_state_manual][AD_ASSERTIVE] = ad4_no_op_setup,
	[ad4_state_manual][AD_BACKLIGHT] = ad4_no_op_setup,
	[ad4_state_manual][AD_STRENGTH] = ad4_strength_setup,
	[ad4_state_manual][AD_ROI] = ad4_roi_setup,
	[ad4_state_manual][AD_IPC_SUSPEND] = ad4_no_op_setup,
	[ad4_state_manual][AD_IPC_RESUME] = ad4_no_op_setup,
	[ad4_state_manual][AD_IPC_RESET] = ad4_setup_debug_manual,
};

struct ad4_info {
	enum ad4_state state;
	u32 completed_ops_mask;
	bool ad4_support;
	enum ad4_modes mode;
	bool is_master;
	u32 last_assertive;
	u32 cached_assertive;
	u32 last_str_inroi;
	u32 last_str_outroi;
	u64 last_als;
	u64 cached_als;
	u64 last_bl;
	u64 cached_bl;
	u32 frame_count;
	u32 frmt_mode;
	u32 irdx_control_0;
	u32 tf_ctrl;
	u32 vc_control_0;
	struct ad4_roi_info last_roi_cfg;
	struct ad4_roi_info cached_roi_cfg;
};

static struct ad4_info info[DSPP_MAX] = {
	[DSPP_0] = {ad4_state_idle, 0, true, AD4_OFF, false, 0x80, 0x80},
	[DSPP_1] = {ad4_state_idle, 0, true, AD4_OFF, false, 0x80, 0x80},
	[DSPP_2] = {ad4_state_max, 0, false, AD4_OFF, false, 0x80, 0x80},
	[DSPP_3] = {ad4_state_max, 0, false, AD4_OFF, false, 0x80, 0x80},
};

void sde_setup_dspp_ad4(struct sde_hw_dspp *dspp, void *ad_cfg)
{
	int ret = 0;
	struct sde_ad_hw_cfg *cfg = ad_cfg;

	ret = ad4_params_check(dspp, ad_cfg);
	if (ret)
		return;

	ret = prop_set_func[info[dspp->idx].state][cfg->prop](dspp, ad_cfg);
	if (ret)
		DRM_ERROR("op failed %d ret %d\n", cfg->prop, ret);
}

int sde_validate_dspp_ad4(struct sde_hw_dspp *dspp, u32 *prop)
{

	if (!dspp || !prop) {
		DRM_ERROR("invalid params dspp %pK prop %pK\n", dspp, prop);
		return -EINVAL;
	}

	if (*prop >= AD_PROPMAX) {
		DRM_ERROR("invalid prop set %d\n", *prop);
		return -EINVAL;
	}

	if (dspp->idx >= DSPP_MAX || !info[dspp->idx].ad4_support) {
		DRM_ERROR("ad4 not supported for dspp idx %d\n", dspp->idx);
		return -EINVAL;
	}

	return 0;
}

static int ad4_params_check(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	struct sde_hw_mixer *hw_lm;

	if (!dspp || !cfg || !cfg->hw_cfg) {
		DRM_ERROR("invalid dspp %pK cfg %pK hw_cfg %pK\n",
			dspp, cfg, ((cfg) ? (cfg->hw_cfg) : NULL));
		return -EINVAL;
	}

	if (!cfg->hw_cfg->mixer_info) {
		DRM_ERROR("invalid mixed info\n");
		return -EINVAL;
	}

	if (dspp->idx >= DSPP_MAX || !info[dspp->idx].ad4_support) {
		DRM_ERROR("ad4 not supported for dspp idx %d\n", dspp->idx);
		return -EINVAL;
	}

	if (cfg->prop >= AD_PROPMAX) {
		DRM_ERROR("invalid prop set %d\n", cfg->prop);
		return -EINVAL;
	}

	if (info[dspp->idx].state >= ad4_state_max) {
		DRM_ERROR("in max state for dspp idx %d\n", dspp->idx);
		return -EINVAL;
	}

	if (!prop_set_func[info[dspp->idx].state][cfg->prop]) {
		DRM_ERROR("prop set not implemented for state %d prop %d\n",
				info[dspp->idx].state, cfg->prop);
		return -EINVAL;
	}

	if (!cfg->hw_cfg->num_of_mixers ||
	    cfg->hw_cfg->num_of_mixers > CRTC_DUAL_MIXERS_ONLY) {
		DRM_ERROR("invalid mixer cnt %d\n",
				cfg->hw_cfg->num_of_mixers);
		return -EINVAL;
	}
	hw_lm = cfg->hw_cfg->mixer_info;
	if (!hw_lm) {
		DRM_ERROR("invalid mixer info\n");
		return -EINVAL;
	}

	if (cfg->hw_cfg->num_of_mixers == 1 &&
	    hw_lm->cfg.out_height != cfg->hw_cfg->displayv &&
	    hw_lm->cfg.out_width != cfg->hw_cfg->displayh) {
		DRM_ERROR("single_lm lmh %d lmw %d displayh %d displayw %d\n",
			hw_lm->cfg.out_height, hw_lm->cfg.out_width,
			cfg->hw_cfg->displayh, cfg->hw_cfg->displayv);
		return -EINVAL;
	} else if (hw_lm->cfg.out_height != cfg->hw_cfg->displayv &&
		   hw_lm->cfg.out_width != (cfg->hw_cfg->displayh >> 1)) {
		DRM_ERROR("dual_lm lmh %d lmw %d displayh %d displayw %d\n",
			hw_lm->cfg.out_height, hw_lm->cfg.out_width,
			cfg->hw_cfg->displayh, cfg->hw_cfg->displayv);
		return -EINVAL;
	}

	return 0;
}

static int ad4_no_op_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
{
	return 0;
}

static int ad4_setup_debug(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
{
	u32 in_str = 0, out_str = 0;
	struct sde_hw_mixer *hw_lm;

	hw_lm = cfg->hw_cfg->mixer_info;
	if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer)
		/* this AD core is the salve core */
		return 0;

	in_str = SDE_REG_READ(&dspp->hw, dspp->cap->sblk->ad.base + 0x4c);
	out_str = SDE_REG_READ(&dspp->hw, dspp->cap->sblk->ad.base + 0x50);
	pr_debug("%s(): AD in strength %d, out strength %d\n", __func__,
				    in_str, out_str);
	return 0;
}

static int ad4_setup_debug_manual(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u32 in_str = 0, out_str = 0;
	struct sde_hw_mixer *hw_lm;

	hw_lm = cfg->hw_cfg->mixer_info;
	if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer)
		/* this AD core is the salve core */
		return 0;

	in_str = SDE_REG_READ(&dspp->hw, dspp->cap->sblk->ad.base + 0x15c);
	out_str = SDE_REG_READ(&dspp->hw, dspp->cap->sblk->ad.base + 0x160);
	pr_debug("%s(): AD in strength = %d, out strength = %d in manual mode\n",
			 __func__, in_str, out_str);

	return 0;
}

static int ad4_mode_setup(struct sde_hw_dspp *dspp, enum ad4_modes mode)
{
	u32 blk_offset;

	if (mode == AD4_OFF) {
		blk_offset = 0x04;
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
				0x101);
		info[dspp->idx].state = ad4_state_idle;
		pr_debug("%s(): AD state move to idle\n", __func__);
		info[dspp->idx].completed_ops_mask = 0;
		/* reset last values to register default */
		info[dspp->idx].last_assertive = 0x80;
		info[dspp->idx].cached_assertive = U8_MAX;
		info[dspp->idx].last_bl = 0xFFFF;
		info[dspp->idx].cached_bl = U64_MAX;
		info[dspp->idx].last_als = 0x0;
		info[dspp->idx].cached_als = U64_MAX;
		info[dspp->idx].last_roi_cfg.h_start = 0x0;
		info[dspp->idx].last_roi_cfg.h_end = 0xffff;
		info[dspp->idx].last_roi_cfg.v_start = 0x0;
		info[dspp->idx].last_roi_cfg.v_end = 0xffff;
		info[dspp->idx].last_roi_cfg.f_in = 0x400;
		info[dspp->idx].last_roi_cfg.f_out = 0x400;
		info[dspp->idx].cached_roi_cfg.h_start = U32_MAX;
		info[dspp->idx].cached_roi_cfg.h_end = U32_MAX;
		info[dspp->idx].cached_roi_cfg.v_start = U32_MAX;
		info[dspp->idx].cached_roi_cfg.v_end = U32_MAX;
		info[dspp->idx].cached_roi_cfg.f_in = U32_MAX;
		info[dspp->idx].cached_roi_cfg.f_out = U32_MAX;
	} else {
		if (mode == AD4_MANUAL) {
			/*vc_control_0 */
			blk_offset = 0x138;
			SDE_REG_WRITE(&dspp->hw,
				dspp->cap->sblk->ad.base + blk_offset, 0);
			/* irdx_control_0 */
			blk_offset = 0x13c;
			SDE_REG_WRITE(&dspp->hw,
				dspp->cap->sblk->ad.base + blk_offset,
				info[dspp->idx].irdx_control_0);
		}
		if (info[dspp->idx].state == ad4_state_idle) {
			if (mode == AD4_MANUAL) {
				info[dspp->idx].state = ad4_state_manual;
				pr_debug("%s(): AD state move to manual\n",
					__func__);
			} else {
				info[dspp->idx].frame_count = 0;
				info[dspp->idx].state = ad4_state_startup;
				pr_debug("%s(): AD state move to startup\n",
					__func__);
			}
		}
		blk_offset = 0x04;
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
				0x100);
	}

	return 0;
}

static int ad4_init_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
{
	u32 frame_start, frame_end, proc_start, proc_end;
	struct sde_hw_mixer *hw_lm;
	u32 blk_offset, tile_ctl, val, i;
	u32 off1, off2, off3, off4, off5, off6;
	struct drm_msm_ad4_init *init;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_init;
		return 0;
	}

	if (cfg->hw_cfg->len != sizeof(struct drm_msm_ad4_init)) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(struct drm_msm_ad4_init), cfg->hw_cfg->len,
			cfg->hw_cfg->payload);
		return -EINVAL;
	}

	hw_lm = cfg->hw_cfg->mixer_info;
	if (cfg->hw_cfg->num_of_mixers == 1) {
		frame_start = 0;
		frame_end = 0xffff;
		proc_start = 0;
		proc_end = 0xffff;
		tile_ctl = 0;
		info[dspp->idx].is_master = true;
	} else {
		tile_ctl = 0x5;
		if (hw_lm->cfg.right_mixer) {
			frame_start = (cfg->hw_cfg->displayh >> 1) -
				MERGE_WIDTH_RIGHT;
			frame_end = cfg->hw_cfg->displayh - 1;
			proc_start = (cfg->hw_cfg->displayh >> 1);
			proc_end = frame_end;
			tile_ctl |= 0x10;
			info[dspp->idx].is_master = false;
		} else {
			frame_start = 0;
			frame_end = (cfg->hw_cfg->displayh >> 1) +
				MERGE_WIDTH_LEFT;
			proc_start = 0;
			proc_end = (cfg->hw_cfg->displayh >> 1) - 1;
			tile_ctl |= 0x10;
			info[dspp->idx].is_master = true;
		}
	}

	init = cfg->hw_cfg->payload;

	info[dspp->idx].frmt_mode = (init->init_param_009 & (BIT(14) - 1));

	blk_offset = 0xc;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			init->init_param_010);

	init->init_param_012 = cfg->hw_cfg->displayv & (BIT(17) - 1);
	init->init_param_011 = cfg->hw_cfg->displayh & (BIT(17) - 1);
	blk_offset = 0x10;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			((init->init_param_011 << 16) | init->init_param_012));

	blk_offset = 0x14;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			tile_ctl);

	blk_offset = 0x44;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			((((init->init_param_013) & (BIT(17) - 1)) << 16) |
			(init->init_param_014 & (BIT(17) - 1))));

	blk_offset = 0x5c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_015 & (BIT(16) - 1)));
	blk_offset = 0x60;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_016 & (BIT(8) - 1)));
	blk_offset = 0x64;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_017 & (BIT(12) - 1)));
	blk_offset = 0x68;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_018 & (BIT(12) - 1)));
	blk_offset = 0x6c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_019 & (BIT(12) - 1)));
	blk_offset = 0x70;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_020 & (BIT(16) - 1)));
	blk_offset = 0x74;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_021 & (BIT(8) - 1)));
	blk_offset = 0x78;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_022 & (BIT(8) - 1)));
	blk_offset = 0x7c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_023 & (BIT(16) - 1)));
	blk_offset = 0x80;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_024 & (BIT(16) - 1)) << 16) |
		((init->init_param_025 & (BIT(16) - 1)))));
	blk_offset = 0x84;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_026 & (BIT(16) - 1)) << 16) |
		((init->init_param_027 & (BIT(16) - 1)))));

	blk_offset = 0x90;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_028 & (BIT(16) - 1)));
	blk_offset = 0x94;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_029 & (BIT(16) - 1)));

	blk_offset = 0x98;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_035 & (BIT(16) - 1)) << 16) |
		((init->init_param_030 & (BIT(16) - 1)))));

	blk_offset = 0x9c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_032 & (BIT(16) - 1)) << 16) |
		((init->init_param_031 & (BIT(16) - 1)))));
	blk_offset = 0xa0;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_034 & (BIT(16) - 1)) << 16) |
		((init->init_param_033 & (BIT(16) - 1)))));

	blk_offset = 0xb4;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_036 & (BIT(8) - 1)));
	blk_offset = 0xcc;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_037 & (BIT(8) - 1)));
	blk_offset = 0xc0;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_038 & (BIT(8) - 1)));
	blk_offset = 0xd8;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_039 & (BIT(8) - 1)));

	blk_offset = 0xe8;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_040 & (BIT(16) - 1)));

	blk_offset = 0xf4;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_041 & (BIT(8) - 1)));

	blk_offset = 0x100;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_042 & (BIT(16) - 1)));

	blk_offset = 0x10c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_043 & (BIT(8) - 1)));

	blk_offset = 0x120;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_044 & (BIT(16) - 1)));
	blk_offset = 0x124;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_045 & (BIT(16) - 1)));

	blk_offset = 0x128;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_046 & (BIT(1) - 1)));
	blk_offset = 0x12c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_047 & (BIT(8) - 1)));

	info[dspp->idx].irdx_control_0 = (init->init_param_048 & (BIT(5) - 1));

	blk_offset = 0x140;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_049 & (BIT(8) - 1)));

	blk_offset = 0x144;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_050 & (BIT(8) - 1)));
	blk_offset = 0x148;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_051 & (BIT(8) - 1)) << 8) |
		((init->init_param_052 & (BIT(8) - 1)))));

	blk_offset = 0x14c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_053 & (BIT(10) - 1)));
	blk_offset = 0x150;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_054 & (BIT(10) - 1)));
	blk_offset = 0x154;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_055 & (BIT(8) - 1)));

	blk_offset = 0x158;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_056 & (BIT(8) - 1)));
	blk_offset = 0x164;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_057 & (BIT(8) - 1)));
	blk_offset = 0x168;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_058 & (BIT(4) - 1)));

	blk_offset = 0x17c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(frame_start & (BIT(16) - 1)));
	blk_offset = 0x180;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(frame_end & (BIT(16) - 1)));
	blk_offset = 0x184;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(proc_start & (BIT(16) - 1)));
	blk_offset = 0x188;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(proc_end & (BIT(16) - 1)));

	blk_offset = 0x18c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_059 & (BIT(4) - 1)));

	blk_offset = 0x190;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		(((init->init_param_061 & (BIT(8) - 1)) << 8) |
		((init->init_param_060 & (BIT(8) - 1)))));

	blk_offset = 0x194;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_062 & (BIT(10) - 1)));

	blk_offset = 0x1a0;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_063 & (BIT(10) - 1)));
	blk_offset = 0x1a4;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_064 & (BIT(10) - 1)));
	blk_offset = 0x1a8;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_065 & (BIT(10) - 1)));
	blk_offset = 0x1ac;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_066 & (BIT(8) - 1)));
	blk_offset = 0x1b0;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_067 & (BIT(8) - 1)));
	blk_offset = 0x1b4;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_068 & (BIT(6) - 1)));

	blk_offset = 0x460;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_069 & (BIT(16) - 1)));
	blk_offset = 0x464;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_070 & (BIT(10) - 1)));
	blk_offset = 0x468;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_071 & (BIT(10) - 1)));
	blk_offset = 0x46c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_072 & (BIT(10) - 1)));
	blk_offset = 0x470;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_073 & (BIT(8) - 1)));
	blk_offset = 0x474;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_074 & (BIT(10) - 1)));
	blk_offset = 0x478;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(init->init_param_075 & (BIT(10) - 1)));

	off1 = 0x1c0;
	off2 = 0x210;
	off3 = 0x260;
	off4 = 0x2b0;
	off5 = 0x380;
	off6 = 0x3d0;
	for (i = 0; i < AD4_LUT_GRP0_SIZE - 1; i = i + 2) {
		val = (init->init_param_001[i] & (BIT(16) - 1));
		val |= ((init->init_param_001[i + 1] & (BIT(16) - 1))
				<< 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off1, val);
		off1 += 4;

		val = (init->init_param_002[i] & (BIT(16) - 1));
		val |= ((init->init_param_002[i + 1] & (BIT(16) - 1))
				<< 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off2, val);
		off2 += 4;

		val = (init->init_param_003[i] & (BIT(16) - 1));
		val |= ((init->init_param_003[i + 1] & (BIT(16) - 1))
				<< 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off3, val);
		off3 += 4;

		val = (init->init_param_004[i] & (BIT(16) - 1));
		val |= ((init->init_param_004[i + 1] & (BIT(16) - 1))
				<< 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off4, val);
		off4 += 4;

		val = (init->init_param_007[i] & (BIT(16) - 1));
		val |= ((init->init_param_007[i + 1] &
				(BIT(16) - 1)) << 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off5, val);
		off5 += 4;

		val = (init->init_param_008[i] & (BIT(12) - 1));
		val |= ((init->init_param_008[i + 1] &
				(BIT(12) - 1)) << 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off6, val);
		off6 += 4;
	}
	/* write last index data */
	i = AD4_LUT_GRP0_SIZE - 1;
	val = ((init->init_param_001[i] & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off1, val);
	val = ((init->init_param_002[i] & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off2, val);
	val = ((init->init_param_003[i] & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off3, val);
	val = ((init->init_param_004[i] & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off4, val);
	val = ((init->init_param_007[i] & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off5, val);
	val = ((init->init_param_008[i] & (BIT(12) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off6, val);

	off1 = 0x300;
	off2 = 0x340;
	for (i = 0; i < AD4_LUT_GRP1_SIZE; i = i + 2) {
		val = (init->init_param_005[i] & (BIT(16) - 1));
		val |= ((init->init_param_005[i + 1] &
				(BIT(16) - 1)) << 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off1, val);
		off1 += 4;

		val = (init->init_param_006[i] & (BIT(16) - 1));
		val |= ((init->init_param_006[i + 1] & (BIT(16) - 1))
				<< 16);
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + off2, val);
		off2 += 4;
	}

	return 0;
}

static int ad4_cfg_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg)
{
	u32 blk_offset, val;
	struct drm_msm_ad4_cfg *ad_cfg;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_cfg;
		return 0;
	}

	if (cfg->hw_cfg->len != sizeof(struct drm_msm_ad4_cfg)) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(struct drm_msm_ad4_cfg), cfg->hw_cfg->len,
			cfg->hw_cfg->payload);
		return -EINVAL;
	}
	ad_cfg = cfg->hw_cfg->payload;

	blk_offset = 0x20;
	val = (ad_cfg->cfg_param_005 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset = 0x24;
	val = (ad_cfg->cfg_param_006 & (BIT(7) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	info[dspp->idx].tf_ctrl = (ad_cfg->cfg_param_008 & (BIT(8) - 1));

	blk_offset = 0x38;
	val = (ad_cfg->cfg_param_009 & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0x3c;
	val = (ad_cfg->cfg_param_010 & (BIT(12) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0x88;
	val = (ad_cfg->cfg_param_013 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_014 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xa4;
	val = (ad_cfg->cfg_param_015 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_016 & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_017 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_018 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xc4;
	val = (ad_cfg->cfg_param_019 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_020 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xb8;
	val = (ad_cfg->cfg_param_021 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_022 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xd0;
	val = (ad_cfg->cfg_param_023 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_024 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xdc;
	val = (ad_cfg->cfg_param_025 & (BIT(16) - 1));
	val |= ((ad_cfg->cfg_param_026 & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_027 & (BIT(16) - 1));
	val |= ((ad_cfg->cfg_param_028 & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_029 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xec;
	val = (ad_cfg->cfg_param_030 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_031 & (BIT(12) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0xf8;
	val = (ad_cfg->cfg_param_032 & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_033 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0x104;
	val = (ad_cfg->cfg_param_034 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_035 & (BIT(12) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0x110;
	val = (ad_cfg->cfg_param_036 & (BIT(12) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_037 & (BIT(12) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_038 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_039 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	blk_offset = 0x134;
	val = (ad_cfg->cfg_param_040 & (BIT(12) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	info[dspp->idx].vc_control_0 = (ad_cfg->cfg_param_041 & (BIT(7) - 1));

	blk_offset = 0x16c;
	val = (ad_cfg->cfg_param_044 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_045 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (ad_cfg->cfg_param_046 & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	return 0;
}

static int ad4_input_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 *val, als;
	u32 blk_offset;

	if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	blk_offset = 0x28;
	if (cfg->hw_cfg->payload) {
		val = cfg->hw_cfg->payload;
	} else {
		als = 0;
		val = &als;
	}
	info[dspp->idx].last_als = (*val & (BIT(16) - 1));
	info[dspp->idx].completed_ops_mask |= ad4_input;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_als);
	return 0;
}

static int ad4_roi_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret = 0;
	u32 blk_offset = 0, val = 0;
	struct ad4_roi_info roi_cfg = {};

	ret = ad4_roi_coordinate_offset(cfg->hw_cfg, &roi_cfg);
	if (ret) {
		DRM_ERROR("params invalid\n");
		return -EINVAL;
	}
	info[dspp->idx].last_roi_cfg = roi_cfg;

	/*roi h start and end*/
	blk_offset = 0x18;
	val = (info[dspp->idx].last_roi_cfg.h_end & (BIT(16) - 1));
	val |= ((info[dspp->idx].last_roi_cfg.h_start & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	/*roi v start and end*/
	blk_offset += 4;
	val = (info[dspp->idx].last_roi_cfg.v_end & (BIT(16) - 1));
	val |= ((info[dspp->idx].last_roi_cfg.v_start & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	/*roi factor in and out*/
	blk_offset = 0x40;
	val = ((info[dspp->idx].last_roi_cfg.f_in & (BIT(16) - 1)) << 16);
	val |= (info[dspp->idx].last_roi_cfg.f_out & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	return ret;
}

static int ad4_roi_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret = 0;
	struct ad4_roi_info roi_cfg = {};

	ret = ad4_roi_coordinate_offset(cfg->hw_cfg, &roi_cfg);
	if (ret) {
		DRM_ERROR("params invalid\n");
		return -EINVAL;
	}

	info[dspp->idx].cached_roi_cfg = roi_cfg;

	return 0;
}

static int ad4_roi_coordinate_offset(struct sde_hw_cp_cfg *hw_cfg,
						struct ad4_roi_info *output)
{
	struct sde_hw_mixer *hw_lm = hw_cfg->mixer_info;
	struct drm_msm_ad4_roi_cfg *roi = NULL;

	if (!hw_cfg->payload) {
		output->h_start = 0x0;
		output->h_end = hw_cfg->displayh;
		output->v_start = 0x0;
		output->v_end = hw_cfg->displayv;
		output->f_in = 0x400;
		output->f_out = 0x400;
		return 0;
	}

	if (hw_cfg->len != sizeof(struct drm_msm_ad4_roi_cfg)) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(struct drm_msm_ad4_roi_cfg), hw_cfg->len,
			hw_cfg->payload);
		return -EINVAL;
	}
	roi = (struct drm_msm_ad4_roi_cfg *)hw_cfg->payload;

	if (roi->h_x >= hw_cfg->displayh || roi->v_x >= hw_cfg->displayv) {
		DRM_ERROR("invalid roi=[%u,%u,%u,%u], display=[%u,%u]\n",
			roi->h_x, roi->h_y, roi->v_x, roi->v_y,
			hw_cfg->displayh, hw_cfg->displayv);
		return -EINVAL;
	}

	if (roi->h_x >= roi->h_y || roi->v_x >= roi->v_y) {
		DRM_ERROR("invalid roi=[%u,%u,%u,%u], display=[%u,%u]\n",
			roi->h_x, roi->h_y, roi->v_x, roi->v_y,
			hw_cfg->displayh, hw_cfg->displayv);
		return -EINVAL;
	}

	if (roi->h_y > hw_cfg->displayh)
		roi->h_y = hw_cfg->displayh;

	if (roi->v_y > hw_cfg->displayv)
		roi->v_y = hw_cfg->displayv;

	/* single dspp cfg */
	output->h_start = roi->h_x;
	output->h_end = roi->h_y;
	output->v_start = roi->v_x;
	output->v_end = roi->v_y;
	output->f_in = roi->factor_in;
	output->f_out = roi->factor_out;

	/* check whether dual dspp */
	if (hw_cfg->num_of_mixers != 2)
		return 0;

	if (roi->h_y <= hw_lm->cfg.out_width) {
		if (hw_lm->cfg.right_mixer) {
			/* the region on the left of screen, clear right info */
			output->h_start = 0;
			output->h_end = 0;
			output->v_start = 0;
			output->v_end = 0;
		}
	} else if (roi->h_x < hw_lm->cfg.out_width) {
		/* the region occupy both sides of screen: left and right */
		if (hw_lm->cfg.right_mixer) {
			output->h_start = 0;
			output->h_end -= (hw_lm->cfg.out_width -
					MERGE_WIDTH_RIGHT);
		} else {
			output->h_end = hw_lm->cfg.out_width;
		}
	} else {
		/* the region on the right of the screen*/
		if (hw_lm->cfg.right_mixer) {
			output->h_start -= (hw_lm->cfg.out_width -
					MERGE_WIDTH_RIGHT);
			output->h_end -= (hw_lm->cfg.out_width -
					MERGE_WIDTH_RIGHT);
		} else {
			output->h_start = 0;
			output->h_end = 0;
			output->v_start = 0;
			output->v_end = 0;
		}
	}
	return 0;
}

static int ad4_suspend_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	info[dspp->idx].state = ad4_state_idle;
	pr_debug("%s(): AD state move to idle\n", __func__);
	info[dspp->idx].completed_ops_mask = 0;
	return 0;
}

static int ad4_mode_setup_common(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{

	if (cfg->hw_cfg->len != sizeof(u64) || !cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	info[dspp->idx].mode = *((enum ad4_modes *)
					(cfg->hw_cfg->payload));
	info[dspp->idx].completed_ops_mask |= ad4_mode;

	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask) ||
					info[dspp->idx].mode == AD4_OFF)
		ad4_mode_setup(dspp, info[dspp->idx].mode);

	return 0;
}

static int ad4_init_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 blk_offset;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_init;
		return 0;
	}

	ret = ad4_init_setup(dspp, cfg);
	if (ret)
		return ret;

	/* enable memory initialization*/
	/* frmt mode */
	blk_offset = 0x8;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(info[dspp->idx].frmt_mode & 0x1fff));
	/* memory init */
	blk_offset = 0x450;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, 0x1);

	/* enforce 0 initial strength when powering up AD config */
	/* irdx_control_0 */
	blk_offset = 0x13c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, 0x6);

	info[dspp->idx].completed_ops_mask |= ad4_init;

	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);

	return 0;
}

static int ad4_init_setup_run(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 blk_offset;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_init;
		return 0;
	}

	ret = ad4_init_setup(dspp, cfg);
	if (ret)
		return ret;

	/* disable memory initialization*/
	/* frmt mode */
	blk_offset = 0x8;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(info[dspp->idx].frmt_mode | 0x2000));
	/* no need to explicitly set memory initialization sequence,
	 * since AD hw were not powered off.
	 */

	/* irdx_control_0 */
	blk_offset = 0x13c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].irdx_control_0);

	return 0;
}

static int ad4_init_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 blk_offset;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_init;
		return 0;
	}

	ret = ad4_init_setup(dspp, cfg);
	if (ret)
		return ret;
	/* no need to explicitly set memory initialization sequence,
	 * since register reset values are the correct configuration
	 */
	/* frmt mode */
	blk_offset = 0x8;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(info[dspp->idx].frmt_mode | 0x2000));
	/* irdx_control_0 */
	blk_offset = 0x13c;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].irdx_control_0);

	info[dspp->idx].completed_ops_mask |= ad4_init;
	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);

	return 0;
}

static int ad4_cfg_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 blk_offset;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_cfg;
		return 0;
	}

	ret = ad4_cfg_setup(dspp, cfg);
	if (ret)
		return ret;

	/* enforce 0 initial strength when powering up AD config */
	/* assertiveness */
	blk_offset = 0x30;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, 0x0);
	/* tf control */
	blk_offset = 0x34;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, 0x55);

	/* vc_control_0 */
	blk_offset = 0x138;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		info[dspp->idx].vc_control_0);

	info[dspp->idx].completed_ops_mask |= ad4_cfg;
	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);
	return 0;
}

static int ad4_cfg_setup_run(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 blk_offset;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_cfg;
		return 0;
	}

	ret = ad4_cfg_setup(dspp, cfg);
	if (ret)
		return ret;

	/* assertiveness */
	blk_offset = 0x30;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_assertive);
	/* tf control */
	blk_offset = 0x34;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		info[dspp->idx].tf_ctrl);
	/* vc_control_0 */
	blk_offset = 0x138;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		info[dspp->idx].vc_control_0);

	return 0;
}

static int ad4_cfg_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 blk_offset;

	if (!cfg->hw_cfg->payload) {
		info[dspp->idx].completed_ops_mask &= ~ad4_cfg;
		return 0;
	}

	ret = ad4_cfg_setup(dspp, cfg);
	if (ret)
		return ret;

	/* assertiveness */
	blk_offset = 0x30;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_assertive);

	info[dspp->idx].completed_ops_mask |= ad4_cfg;
	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);
	return 0;
}

static int ad4_input_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;

	ret = ad4_input_setup(dspp, cfg);
	if (ret)
		return ret;

	info[dspp->idx].completed_ops_mask |= ad4_input;
	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);

	return 0;
}

static int ad4_input_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 *val, als;
	u32 blk_offset;

	if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	blk_offset = 0x28;
	if (cfg->hw_cfg->payload) {
		val = cfg->hw_cfg->payload;
	} else {
		als = 0;
		val = &als;
	}
	info[dspp->idx].cached_als = *val & (BIT(16) - 1);
	info[dspp->idx].completed_ops_mask |= ad4_input;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_als);

	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);

	return 0;
}

static int ad4_assertive_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 *val, assertive;
	u32 blk_offset;

	if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	blk_offset = 0x30;
	if (cfg->hw_cfg->payload) {
		val = cfg->hw_cfg->payload;
	} else {
		assertive = 0;
		val = &assertive;
	}

	info[dspp->idx].last_assertive = *val & (BIT(8) - 1);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			(info[dspp->idx].last_assertive));
	return 0;
}

static int ad4_assertive_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 *val, assertive;
	u32 blk_offset;

	if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	blk_offset = 0x30;
	if (cfg->hw_cfg->payload) {
		val = cfg->hw_cfg->payload;
	} else {
		assertive = 0;
		val = &assertive;
	}

	info[dspp->idx].cached_assertive = *val & (BIT(8) - 1);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_assertive);

	return 0;
}

static int ad4_backlight_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 *val, bl;
	u32 blk_offset;

	if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	blk_offset = 0x2c;
	if (cfg->hw_cfg->payload) {
		val = cfg->hw_cfg->payload;
	} else {
		bl = 0;
		val = &bl;
	}

	info[dspp->idx].last_bl = *val & (BIT(16) - 1);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_bl);
	return 0;
}

static int ad4_backlight_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 *val, bl;
	u32 blk_offset;

	if (cfg->hw_cfg->len != sizeof(u64) && cfg->hw_cfg->payload) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
			sizeof(u64), cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	blk_offset = 0x2c;
	if (cfg->hw_cfg->payload) {
		val = cfg->hw_cfg->payload;
	} else {
		bl = 0;
		val = &bl;
	}

	info[dspp->idx].cached_bl = *val & (BIT(16) - 1);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
			info[dspp->idx].last_bl);

	return 0;
}

void sde_read_intr_resp_ad4(struct sde_hw_dspp *dspp, u32 event,
		u32 *resp_in, u32 *resp_out)
{
	if (!dspp || !resp_in || !resp_out) {
		DRM_ERROR("invalid params dspp %pK resp_in %pK resp_out %pK\n",
				dspp, resp_in, resp_out);
		return;
	}

	switch (event) {
	case AD4_IN_OUT_BACKLIGHT:
		*resp_in = SDE_REG_READ(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x2c);
		*resp_out = SDE_REG_READ(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x48);
		pr_debug("%s(): AD4 input BL %u, output BL %u\n", __func__,
			(*resp_in), (*resp_out));
		break;
	default:
		break;
	}
}

static int ad4_ipc_suspend_setup_run(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u32 in_str = 0, out_str = 0, i = 0;
	struct sde_hw_mixer *hw_lm;

	hw_lm = cfg->hw_cfg->mixer_info;
	if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer) {
		/* this AD core is the salve core */
		for (i = DSPP_0; i < DSPP_MAX; i++) {
			if (info[i].is_master) {
				in_str = info[i].last_str_inroi;
				out_str = info[i].last_str_outroi;
				break;
			}
		}
	} else {
		in_str = SDE_REG_READ(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x4c);
		out_str = SDE_REG_READ(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x50);
		pr_debug("%s(): AD in strength %d, out %d\n", __func__,
				in_str, out_str);
	}
	info[dspp->idx].last_str_inroi = in_str;
	info[dspp->idx].last_str_outroi = out_str;
	info[dspp->idx].state = ad4_state_ipcs;
	pr_debug("%s(): AD state move to ipcs\n", __func__);

	return 0;
}

static int ad4_ipc_resume_setup_ipcs(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u32 blk_offset, val;

	info[dspp->idx].frame_count = 0;
	info[dspp->idx].state = ad4_state_ipcr;
	pr_debug("%s(): AD state move to ipcr\n", __func__);

	/* no need to rewrite frmt_mode bit 13 and mem_init,
	 * since the default register values are exactly what
	 * we wanted.
	 */

	/* ipc resume with manual strength */
	/* tf control */
	blk_offset = 0x34;
	val = (0x55 & (BIT(8) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	/* set roi config */
	blk_offset = 0x18;
	val = (info[dspp->idx].last_roi_cfg.h_end & (BIT(16) - 1));
	val |= ((info[dspp->idx].last_roi_cfg.h_start & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (info[dspp->idx].last_roi_cfg.v_end & (BIT(16) - 1));
	val |= ((info[dspp->idx].last_roi_cfg.v_start & (BIT(16) - 1)) << 16);
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset = 0x40;
	val = ((info[dspp->idx].last_roi_cfg.f_in & (BIT(16) - 1)) << 16);
	val |= (info[dspp->idx].last_roi_cfg.f_out & (BIT(16) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	/* set manual strength */
	blk_offset = 0x15c;
	val = (info[dspp->idx].last_str_inroi & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset = 0x160;
	val = (info[dspp->idx].last_str_outroi & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	/* enable manual mode */
	blk_offset = 0x138;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, 0);

	return 0;
}

static int ad4_ipc_suspend_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	info[dspp->idx].state = ad4_state_ipcs;
	pr_debug("%s(): AD state move to ipcs\n", __func__);
	return 0;
}

static int ad4_ipc_reset_setup_ipcr(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;
	u32 in_str = 0, out_str = 0, i = 0;
	struct sde_hw_mixer *hw_lm;

	/* Read AD calculator strength output during the 2 frames of manual
	 * strength mode, and assign the strength output to last_str
	 * when frame count reaches AD_IPC_FRAME_COUNT to avoid flickers
	 * caused by strength was not converged before entering IPC mode
	 */
	hw_lm = cfg->hw_cfg->mixer_info;
	if ((cfg->hw_cfg->num_of_mixers == 2) && hw_lm->cfg.right_mixer) {
		/* this AD core is the salve core */
		for (i = DSPP_0; i < DSPP_MAX; i++) {
			if (info[i].is_master) {
				in_str = info[i].last_str_inroi;
				out_str = info[i].last_str_outroi;
				break;
			}
		}
	} else {
		in_str = SDE_REG_READ(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x4c);
		out_str = SDE_REG_READ(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x50);
		pr_debug("%s(): AD in strength %d, out %d\n", __func__,
				in_str, out_str);
	}

	if (info[dspp->idx].frame_count == AD_IPC_FRAME_COUNT) {
		info[dspp->idx].state = ad4_state_run;
		pr_debug("%s(): AD state move to run\n", __func__);
		info[dspp->idx].last_str_inroi = in_str;
		info[dspp->idx].last_str_outroi = out_str;
		ret = ad4_cfg_ipc_reset(dspp, cfg);
		if (ret)
			return ret;
	} else {
		info[dspp->idx].frame_count++;
	}

	return 0;
}

static int ad4_cfg_ipc_reset(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u32 blk_offset, val = 0;

	/* revert manual strength */
	/* tf control */
	blk_offset = 0x34;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		info[dspp->idx].tf_ctrl);
	/* vc_control_0 */
	blk_offset = 0x138;
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
		info[dspp->idx].vc_control_0);

	/* reset cached ALS, backlight and assertiveness */
	if (info[dspp->idx].cached_als != U64_MAX) {
		SDE_REG_WRITE(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x28,
				info[dspp->idx].cached_als);
		info[dspp->idx].last_als = info[dspp->idx].cached_als;
		info[dspp->idx].cached_als = U64_MAX;
	}
	if (info[dspp->idx].cached_bl != U64_MAX) {
		SDE_REG_WRITE(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x2c,
				info[dspp->idx].cached_bl);
		info[dspp->idx].last_bl = info[dspp->idx].cached_bl;
		info[dspp->idx].cached_bl = U64_MAX;
	}
	if (info[dspp->idx].cached_assertive != U8_MAX) {
		SDE_REG_WRITE(&dspp->hw,
				dspp->cap->sblk->ad.base + 0x30,
				info[dspp->idx].cached_assertive);
		info[dspp->idx].last_assertive =
				info[dspp->idx].cached_assertive;
		info[dspp->idx].cached_assertive = U8_MAX;
	}

	/*reset cached roi config*/
	if (info[dspp->idx].cached_roi_cfg.h_start != U32_MAX) {
		blk_offset = 0x18;
		val = (info[dspp->idx].cached_roi_cfg.h_end & (BIT(16) - 1));
		val |= ((info[dspp->idx].cached_roi_cfg.h_start &
			(BIT(16) - 1)) << 16);
		SDE_REG_WRITE(&dspp->hw,
			dspp->cap->sblk->ad.base + blk_offset, val);
		blk_offset += 4;
		val = (info[dspp->idx].cached_roi_cfg.v_end & (BIT(16) - 1));
		val |= ((info[dspp->idx].cached_roi_cfg.v_start &
			(BIT(16) - 1)) << 16);
		SDE_REG_WRITE(&dspp->hw,
			dspp->cap->sblk->ad.base + blk_offset, val);
		blk_offset = 0x40;
		val = ((info[dspp->idx].cached_roi_cfg.f_in &
			(BIT(16) - 1)) << 16);
		val |= (info[dspp->idx].cached_roi_cfg.f_out & (BIT(16) - 1));
		SDE_REG_WRITE(&dspp->hw,
			dspp->cap->sblk->ad.base + blk_offset, val);

		info[dspp->idx].last_roi_cfg = info[dspp->idx].cached_roi_cfg;
		info[dspp->idx].cached_roi_cfg.h_start = U32_MAX;
		info[dspp->idx].cached_roi_cfg.h_end = U32_MAX;
		info[dspp->idx].cached_roi_cfg.v_start = U32_MAX;
		info[dspp->idx].cached_roi_cfg.v_end = U32_MAX;
		info[dspp->idx].cached_roi_cfg.f_in = U32_MAX;
		info[dspp->idx].cached_roi_cfg.f_out = U32_MAX;
	}
	return 0;
}

static int ad4_ipc_reset_setup_startup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u32 blk_offset;

	if (info[dspp->idx].frame_count == AD_IPC_FRAME_COUNT) {
		info[dspp->idx].state = ad4_state_run;
		pr_debug("%s(): AD state move to run\n", __func__);

		/* revert enforce 0 initial strength */
		/* irdx_control_0 */
		blk_offset = 0x13c;
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
				info[dspp->idx].irdx_control_0);
		/* assertiveness */
		blk_offset = 0x30;
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
				info[dspp->idx].last_assertive);
		/* tf control */
		blk_offset = 0x34;
		SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset,
				info[dspp->idx].tf_ctrl);
	} else {
		info[dspp->idx].frame_count++;
	}

	return 0;
}

static int ad4_strength_setup(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	u64 in_str = 0, out_str = 0, val;
	u32 blk_offset = 0x15c;
	struct drm_msm_ad4_manual_str_cfg *str_cfg = NULL;

	if (cfg->hw_cfg->payload && (cfg->hw_cfg->len !=
		sizeof(struct drm_msm_ad4_manual_str_cfg))) {
		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
				sizeof(struct drm_msm_ad4_manual_str_cfg),
				cfg->hw_cfg->len, cfg->hw_cfg->payload);
		return -EINVAL;
	}

	if (cfg->hw_cfg->payload) {
		str_cfg = (struct drm_msm_ad4_manual_str_cfg *)
			cfg->hw_cfg->payload;
		in_str = str_cfg->in_str;
		out_str = str_cfg->out_str;
	}

	/* set manual strength */
	info[dspp->idx].completed_ops_mask |= ad4_strength;
	val = (in_str & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
	blk_offset += 4;
	val = (out_str & (BIT(10) - 1));
	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);

	return 0;
}

static int ad4_strength_setup_idle(struct sde_hw_dspp *dspp,
		struct sde_ad_hw_cfg *cfg)
{
	int ret;

	ret = ad4_strength_setup(dspp, cfg);
	if (ret)
		return ret;

	if (AD_STATE_READY(info[dspp->idx].completed_ops_mask))
		ad4_mode_setup(dspp, info[dspp->idx].mode);
	return 0;
}
