// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include #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; }