Merge "disp: msm: sde: pass sde_enc to sde_encoder_control_te"

This commit is contained in:
qctecmdr
2022-10-28 18:12:45 -07:00
committed by Gerrit - the friendly Code Review server
9 changed files with 123 additions and 47 deletions

View File

@@ -5040,18 +5040,8 @@ static void sde_crtc_handle_power_event(u32 event_type, void *arg)
break;
case SDE_POWER_EVENT_PRE_DISABLE:
drm_for_each_encoder_mask(encoder, crtc->dev,
crtc->state->encoder_mask) {
/*
* disable the vsync source after updating the
* rsc state. rsc state update might have vsync wait
* and vsync source must be disabled after it.
* It will avoid generating any vsync from this point
* till mode-2 entry. It is SW workaround for HW
* limitation and should not be removed without
* checking the updated design.
*/
sde_encoder_control_te(encoder, false);
}
crtc->state->encoder_mask)
sde_encoder_idle_pc_enter(encoder);
spin_lock_irqsave(&sde_crtc->spin_lock, flags);
node = NULL;

View File

@@ -1453,6 +1453,22 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc,
}
}
static void sde_encoder_control_te(struct sde_encoder_virt *sde_enc, bool enable)
{
struct sde_encoder_phys *phys;
int i;
if (!sde_enc) {
SDE_ERROR("invalid sde encoder\n");
return;
}
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
if (phys && phys->ops.control_te)
phys->ops.control_te(phys, enable);
}
}
int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc,
@@ -1468,13 +1484,13 @@ int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc,
sde_enc = to_sde_encoder_virt(drm_enc);
sde_encoder_control_te(drm_enc, false);
sde_encoder_control_te(sde_enc, false);
memcpy(&disp_info, &sde_enc->disp_info, sizeof(disp_info));
disp_info.is_te_using_watchdog_timer = watchdog_te;
_sde_encoder_update_vsync_source(sde_enc, &disp_info);
sde_encoder_control_te(drm_enc, true);
sde_encoder_control_te(sde_enc, true);
return 0;
}
@@ -2183,9 +2199,9 @@ static int _sde_encoder_rc_post_modeset(struct drm_encoder *drm_enc,
/* toggle te bit to update vsync source for sim cmd mode panels */
if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_CMD_MODE)
&& sde_enc->disp_info.is_te_using_watchdog_timer) {
sde_encoder_control_te(drm_enc, false);
sde_encoder_control_te(sde_enc, false);
_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info);
sde_encoder_control_te(drm_enc, true);
sde_encoder_control_te(sde_enc, true);
}
_sde_encoder_update_rsc_client(drm_enc, true);
@@ -2728,28 +2744,28 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
sde_encoder_virt_modeset_rc(drm_enc, adj_mode, msm_mode, false);
}
void sde_encoder_control_te(struct drm_encoder *drm_enc, bool enable)
void sde_encoder_idle_pc_enter(struct drm_encoder *drm_enc)
{
struct sde_encoder_virt *sde_enc;
struct sde_encoder_phys *phys;
int i;
struct sde_encoder_virt *sde_enc = NULL;
if (!drm_enc) {
SDE_ERROR("invalid parameters\n");
SDE_ERROR("invalid encoder\n");
return;
}
sde_enc = to_sde_encoder_virt(drm_enc);
if (!sde_enc) {
SDE_ERROR("invalid sde encoder\n");
return;
}
/*
* disable the vsync source after updating the
* rsc state. rsc state update might have vsync wait
* and vsync source must be disabled after it.
* It will avoid generating any vsync from this point
* till mode-2 entry. It is SW workaround for HW
* limitation and should not be removed without
* checking the updated design.
*/
sde_encoder_control_te(sde_enc, false);
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
if (phys && phys->ops.control_te)
phys->ops.control_te(phys, enable);
}
if (sde_enc->cur_master && sde_enc->cur_master->ops.idle_pc_cache_display_status)
sde_enc->cur_master->ops.idle_pc_cache_display_status(sde_enc->cur_master);
}
static int _sde_encoder_input_connect(struct input_handler *handler,
@@ -3022,7 +3038,7 @@ void sde_encoder_virt_restore(struct drm_encoder *drm_enc)
sde_enc->cur_master->ops.restore(sde_enc->cur_master);
_sde_encoder_virt_enable_helper(drm_enc);
sde_encoder_control_te(drm_enc, true);
sde_encoder_control_te(sde_enc, true);
/*
* During IPC misr ctl register is reset.
@@ -3180,11 +3196,11 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
sizeof(sde_enc->cur_master->intf_cfg_v1));
/* turn off vsync_in to update tear check configuration */
sde_encoder_control_te(drm_enc, false);
sde_encoder_control_te(sde_enc, false);
sde_encoder_populate_encoder_phys(drm_enc, sde_enc, msm_mode);
_sde_encoder_virt_enable_helper(drm_enc);
sde_encoder_control_te(drm_enc, true);
sde_encoder_control_te(sde_enc, true);
}
void sde_encoder_virt_reset(struct drm_encoder *drm_enc)

View File

@@ -396,11 +396,10 @@ bool sde_encoder_get_vblank_timestamp(struct drm_encoder *encoder,
ktime_t *tvblank);
/**
* sde_encoder_control_te - control enabling/disabling VSYNC_IN_EN
* sde_encoder_idle_pc_enter - control enable/disable VSYNC_IN_EN & cache display status at ipc
* @encoder: encoder pointer
* @enable: boolean to indicate enable/disable
*/
void sde_encoder_control_te(struct drm_encoder *encoder, bool enable);
void sde_encoder_idle_pc_enter(struct drm_encoder *encoder);
/**
* sde_encoder_virt_restore - restore the encoder configs

View File

@@ -140,6 +140,7 @@ struct sde_encoder_virt_ops {
* count and underrun line count
* @add_to_minidump: Add this phys_enc data to minidumps
* @disable_autorefresh: Disable autorefresh
* @idle_pc_cache_display_status: caches display status at idle power collapse
*/
struct sde_encoder_phys_ops {
@@ -194,6 +195,7 @@ struct sde_encoder_phys_ops {
u32 (*get_underrun_line_count)(struct sde_encoder_phys *phys);
void (*add_to_minidump)(struct sde_encoder_phys *phys);
void (*disable_autorefresh)(struct sde_encoder_phys *phys);
void (*idle_pc_cache_display_status)(struct sde_encoder_phys *phys);
};
/**
@@ -275,6 +277,7 @@ struct sde_encoder_irq {
* @hw_dnsc_blur: Hardware interface to the downscale blur registers
* @sde_kms: Pointer to the sde_kms top level
* @cached_mode: DRM mode cached at mode_set time, acted on in enable
* @wd_jitter : Pointer to watchdog jitter prams
* @enabled: Whether the encoder has enabled and running a mode
* @split_role: Role to play in a split-panel configuration
* @intf_mode: Interface mode
@@ -333,6 +336,7 @@ struct sde_encoder_phys {
struct sde_hw_dnsc_blur *hw_dnsc_blur;
struct sde_kms *sde_kms;
struct drm_display_mode cached_mode;
struct intf_wd_jitter_params wd_jitter;
enum sde_enc_split_role split_role;
enum sde_intf_mode intf_mode;
enum sde_intf intf_idx;

View File

@@ -2037,30 +2037,42 @@ static void sde_encoder_phys_cmd_trigger_start(
cmd_enc->wr_ptr_wait_success = false;
}
static void _sde_encoder_phys_cmd_calculate_wd_params(struct sde_encoder_phys *phys_enc,
struct intf_wd_jitter_params *wd_jitter)
static void _sde_encoder_phys_cmd_calculate_wd_params(struct sde_encoder_phys *phys_enc)
{
u32 nominal_te_value;
struct sde_encoder_virt *sde_enc;
struct msm_mode_info *mode_info;
const u32 multiplier = 1 << 10;
struct intf_wd_jitter_params wd_jtr;
sde_enc = to_sde_encoder_virt(phys_enc->parent);
mode_info = &sde_enc->mode_info;
if (mode_info->wd_jitter.jitter_type & MSM_DISPLAY_WD_INSTANTANEOUS_JITTER)
wd_jitter->jitter = mult_frac(multiplier, mode_info->wd_jitter.inst_jitter_numer,
if (mode_info->wd_jitter.jitter_type & MSM_DISPLAY_WD_INSTANTANEOUS_JITTER) {
wd_jtr.jitter = mult_frac(multiplier,
mode_info->wd_jitter.inst_jitter_numer,
(mode_info->wd_jitter.inst_jitter_denom * 100));
phys_enc->wd_jitter.jitter = wd_jtr.jitter;
}
if (mode_info->wd_jitter.jitter_type & MSM_DISPLAY_WD_LTJ_JITTER) {
nominal_te_value = CALCULATE_WD_LOAD_VALUE(mode_info->frame_rate) * MDP_TICK_COUNT;
wd_jitter->ltj_max = mult_frac(nominal_te_value, mode_info->wd_jitter.ltj_max_numer,
wd_jtr.ltj_max = mult_frac(nominal_te_value,
mode_info->wd_jitter.ltj_max_numer,
(mode_info->wd_jitter.ltj_max_denom) * 100);
wd_jitter->ltj_slope = mult_frac((1 << 16), wd_jitter->ltj_max,
wd_jtr.ltj_slope = mult_frac((1 << 16), wd_jtr.ltj_max,
(mode_info->wd_jitter.ltj_time_sec * mode_info->frame_rate));
phys_enc->wd_jitter.ltj_max = wd_jtr.ltj_max;
phys_enc->wd_jitter.ltj_slope = wd_jtr.ltj_slope;
}
phys_enc->hw_intf->ops.configure_wd_jitter(phys_enc->hw_intf, wd_jitter);
phys_enc->hw_intf->ops.configure_wd_jitter(phys_enc->hw_intf, &phys_enc->wd_jitter);
}
static void sde_encoder_phys_cmd_store_ltj_values(struct sde_encoder_phys *phys_enc)
{
if (phys_enc && phys_enc->hw_intf->ops.get_wd_ltj_status)
phys_enc->hw_intf->ops.get_wd_ltj_status(phys_enc->hw_intf, &phys_enc->wd_jitter);
}
static void sde_encoder_phys_cmd_setup_vsync_source(struct sde_encoder_phys *phys_enc,
@@ -2068,7 +2080,6 @@ static void sde_encoder_phys_cmd_setup_vsync_source(struct sde_encoder_phys *phy
{
struct sde_encoder_virt *sde_enc;
struct sde_connector *sde_conn;
struct intf_wd_jitter_params wd_jitter = {0, 0};
if (!phys_enc || !phys_enc->hw_intf)
return;
@@ -2083,7 +2094,7 @@ static void sde_encoder_phys_cmd_setup_vsync_source(struct sde_encoder_phys *phy
phys_enc->hw_intf->ops.setup_vsync_source) {
vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0;
if (phys_enc->hw_intf->ops.configure_wd_jitter)
_sde_encoder_phys_cmd_calculate_wd_params(phys_enc, &wd_jitter);
_sde_encoder_phys_cmd_calculate_wd_params(phys_enc);
phys_enc->hw_intf->ops.setup_vsync_source(phys_enc->hw_intf,
sde_enc->mode_info.frame_rate);
} else {
@@ -2136,6 +2147,7 @@ static void sde_encoder_phys_cmd_init_ops(struct sde_encoder_phys_ops *ops)
ops->collect_misr = sde_encoder_helper_collect_misr;
ops->add_to_minidump = sde_encoder_phys_cmd_add_enc_to_minidump;
ops->disable_autorefresh = _sde_encoder_phys_disable_autorefresh;
ops->idle_pc_cache_display_status = sde_encoder_phys_cmd_store_ltj_values;
}
static inline bool sde_encoder_phys_cmd_intf_te_supported(

View File

@@ -2493,6 +2493,7 @@ static int sde_intf_parse_dt(struct device_node *np,
set_bit(SDE_INTF_MDP_VSYNC_FC, &intf->features);
set_bit(SDE_INTF_TE_32BIT, &intf->features);
set_bit(SDE_INTF_TE_SINGLE_UPDATE, &intf->features);
set_bit(SDE_INTF_WD_LTJ_CTL, &intf->features);
}
}

View File

@@ -606,6 +606,7 @@ enum {
* @SDE_INTF_MDP_VSYNC_FC INTF block has mdp vsync frame counter
* @SDE_INTF_AVR_STATUS INTF block has AVR_STATUS field in AVR_CONTROL register
* @SDE_INTF_WD_JITTER INTF block has WD timer jitter support
* @SDE_INTF_WD_LTJ_CTL INTF block has WD long term jitter control support
* @SDE_INTF_MAX
*/
enum {
@@ -622,6 +623,7 @@ enum {
SDE_INTF_MDP_VSYNC_FC,
SDE_INTF_AVR_STATUS,
SDE_INTF_WD_JITTER,
SDE_INTF_WD_LTJ_CTL,
SDE_INTF_MAX
};

View File

@@ -65,6 +65,8 @@
#define INTF_MISR_CTRL 0x180
#define INTF_MISR_SIGNATURE 0x184
#define INTF_WD_TIMER_0_LTJ_CTL 0x200
#define INTF_WD_TIMER_0_LTJ_CTL1 0x204
#define INTF_VSYNC_TIMESTAMP_CTRL 0x210
#define INTF_VSYNC_TIMESTAMP0 0x214
#define INTF_VSYNC_TIMESTAMP1 0x218
@@ -76,6 +78,8 @@
#define INTF_WD_TIMER_0_CTL 0x230
#define INTF_WD_TIMER_0_CTL2 0x234
#define INTF_WD_TIMER_0_LOAD_VALUE 0x238
#define INTF_WD_TIMER_0_LTJ_INT_STATUS 0x240
#define INTF_WD_TIMER_0_LTJ_FRAC_STATUS 0x244
#define INTF_MUX 0x25C
#define INTF_UNDERRUN_COUNT 0x268
#define INTF_STATUS 0x26C
@@ -496,6 +500,40 @@ static void sde_hw_intf_configure_wd_timer_jitter(struct sde_hw_intf *intf,
if (wd_jitter->ltj_max)
reg |= BIT(30);
SDE_REG_WRITE(c, INTF_WD_TIMER_0_JITTER_CTL, reg);
if (intf->cap->features & BIT(SDE_INTF_WD_LTJ_CTL)) {
if (wd_jitter->ltj_step_dir && wd_jitter->ltj_initial_val) {
reg = ((wd_jitter->ltj_step_dir & 0x1) << 31) |
(wd_jitter->ltj_initial_val & 0x1FFFFF);
SDE_REG_WRITE(c, INTF_WD_TIMER_0_LTJ_CTL, reg);
wd_jitter->ltj_step_dir = 0;
wd_jitter->ltj_initial_val = 0;
}
if (wd_jitter->ltj_fractional_val) {
SDE_REG_WRITE(c, INTF_WD_TIMER_0_LTJ_CTL1, wd_jitter->ltj_fractional_val);
wd_jitter->ltj_fractional_val = 0;
}
}
}
static void sde_hw_intf_read_wd_ltj_ctl(struct sde_hw_intf *intf,
struct intf_wd_jitter_params *wd_jitter)
{
struct sde_hw_blk_reg_map *c;
u32 reg;
c = &intf->hw;
if (intf->cap->features & BIT(SDE_INTF_WD_LTJ_CTL)) {
reg = SDE_REG_READ(c, INTF_WD_TIMER_0_LTJ_INT_STATUS);
wd_jitter->ltj_step_dir = reg & BIT(31);
wd_jitter->ltj_initial_val = (reg & 0x1FFFFF);
reg = SDE_REG_READ(c, INTF_WD_TIMER_0_LTJ_FRAC_STATUS);
wd_jitter->ltj_fractional_val = (reg & 0xFFFF);
}
}
static void sde_hw_intf_setup_vsync_source(struct sde_hw_intf *intf, u32 frame_rate)
@@ -1039,6 +1077,9 @@ static void _setup_intf_ops(struct sde_hw_intf_ops *ops,
if (cap & BIT(SDE_INTF_WD_JITTER))
ops->configure_wd_jitter = sde_hw_intf_configure_wd_timer_jitter;
if (cap & BIT(SDE_INTF_WD_LTJ_CTL))
ops->get_wd_ltj_status = sde_hw_intf_read_wd_ltj_ctl;
}
struct sde_hw_blk_reg_map *sde_hw_intf_init(enum sde_intf idx,

View File

@@ -73,13 +73,18 @@ struct intf_avr_params {
* jitter : max instantaneous jitter.
* ltj_max : max long term jitter value.
* ltj_slope : slope of long term jitter.
*ltj_step_dir: direction of the step in LTJ
*ltj_initial_val: LTJ initial value
*ltj_fractional_val: LTJ fractional initial value
*/
struct intf_wd_jitter_params {
u32 jitter;
u32 ltj_max;
u32 ltj_slope;
u8 ltj_step_dir;
u32 ltj_initial_val;
u32 ltj_fractional_val;
};
/**
* struct sde_hw_intf_ops : Interface to the interface Hw driver functions
* Assumption is these functions will be called after clocks are enabled
@@ -95,6 +100,8 @@ struct intf_wd_jitter_params {
* converts it into line count
* @setup_vsync_source: Configure vsync source selection for intf
* @configure_wd_jitter: Configure WD jitter.
* @ write_wd_ltj: Write WD long term jitter.
* @get_wd_ltj_status: Read WD long term jitter status.
* @bind_pingpong_blk: enable/disable the connection with pingpong which will
* feed pixels to this interface
*/
@@ -132,6 +139,10 @@ struct sde_hw_intf_ops {
void (*setup_vsync_source)(struct sde_hw_intf *intf, u32 frame_rate);
void (*configure_wd_jitter)(struct sde_hw_intf *intf,
struct intf_wd_jitter_params *wd_jitter);
void (*write_wd_ltj)(struct sde_hw_intf *intf,
struct intf_wd_jitter_params *wd_jitter);
void (*get_wd_ltj_status)(struct sde_hw_intf *intf,
struct intf_wd_jitter_params *wd_jitter);
void (*bind_pingpong_blk)(struct sde_hw_intf *intf,
bool enable,