drm/msm/sde: enable ping pong split support
Add support for using a single layer mixer to drive two video or command mode interfaces. This configuration frees up one of the layer mixer paths for other use cases/displays. Change-Id: I3a0ca07e37f121bf56ea8984c0cca25af7bc647e Signed-off-by: Clarence Ip <cip@codeaurora.org>
This commit is contained in:
parent
4f7d7c780e
commit
ed93daaea7
11 changed files with 230 additions and 111 deletions
|
@ -57,6 +57,8 @@ Required properties
|
|||
- qcom,sde-pp-off: Array of offset addresses for the available
|
||||
pingpong blocks. These offsets are calculated
|
||||
from register "mdp_phys" defined in reg property.
|
||||
- qcom,sde-pp-slave: Array of flags indicating whether each ping pong
|
||||
block may be configured as a pp slave.
|
||||
- qcom,sde-intf-off: Array of offset addresses for the available SDE
|
||||
interface blocks that can drive data to a
|
||||
panel controller. The offsets are calculated
|
||||
|
@ -286,6 +288,7 @@ Example:
|
|||
qcom,sde-intf-type = "none", "dsi", "dsi", "hdmi";
|
||||
qcom,sde-pp-off = <0x00071000 0x00071800
|
||||
0x00072000 0x00072800>;
|
||||
qcom,sde-pp-slave = <0x0 0x0 0x0 0x0>;
|
||||
qcom,sde-cdm-off = <0x0007a200>;
|
||||
qcom,sde-dsc-off = <0x00081000 0x00081400>;
|
||||
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
|
||||
|
|
|
@ -290,8 +290,11 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
|
|||
/* stage config flush mask */
|
||||
ctl->ops.update_pending_flush(ctl, mixer[i].flush_mask);
|
||||
|
||||
SDE_DEBUG("lm %d ctl %d add mask 0x%x to pending flush\n",
|
||||
mixer[i].hw_lm->idx, ctl->idx, mixer[i].flush_mask);
|
||||
SDE_DEBUG("lm %d, op_mode 0x%X, ctl %d, flush mask 0x%x\n",
|
||||
mixer[i].hw_lm->idx - LM_0,
|
||||
mixer[i].mixer_op_mode,
|
||||
ctl->idx - CTL_0,
|
||||
mixer[i].flush_mask);
|
||||
|
||||
ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx,
|
||||
&sde_crtc->stage_cfg, i);
|
||||
|
@ -492,7 +495,7 @@ static void _sde_crtc_setup_mixer_for_encoder(
|
|||
/* CTL may be <= LMs, if <, multiple LMs controlled by 1 CTL */
|
||||
if (!sde_rm_get_hw(rm, &ctl_iter)) {
|
||||
SDE_DEBUG("no ctl assigned to lm %d, using previous\n",
|
||||
mixer->hw_lm->idx);
|
||||
mixer->hw_lm->idx - LM_0);
|
||||
mixer->hw_ctl = last_valid_ctl;
|
||||
} else {
|
||||
mixer->hw_ctl = (struct sde_hw_ctl *)ctl_iter.hw;
|
||||
|
@ -502,7 +505,7 @@ static void _sde_crtc_setup_mixer_for_encoder(
|
|||
/* Shouldn't happen, mixers are always >= ctls */
|
||||
if (!mixer->hw_ctl) {
|
||||
SDE_ERROR("no valid ctls found for lm %d\n",
|
||||
mixer->hw_lm->idx);
|
||||
mixer->hw_lm->idx - LM_0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -244,6 +244,57 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc)
|
|||
kfree(sde_enc);
|
||||
}
|
||||
|
||||
void sde_encoder_helper_split_config(
|
||||
struct sde_encoder_phys *phys_enc,
|
||||
enum sde_intf interface)
|
||||
{
|
||||
struct sde_encoder_virt *sde_enc;
|
||||
struct split_pipe_cfg cfg = { 0 };
|
||||
struct sde_hw_mdp *hw_mdptop;
|
||||
enum sde_rm_topology_name topology;
|
||||
|
||||
if (!phys_enc || !phys_enc->hw_mdptop || !phys_enc->parent) {
|
||||
SDE_ERROR("invalid arg(s), encoder %d\n", phys_enc != 0);
|
||||
return;
|
||||
}
|
||||
|
||||
sde_enc = to_sde_encoder_virt(phys_enc->parent);
|
||||
hw_mdptop = phys_enc->hw_mdptop;
|
||||
cfg.en = phys_enc->split_role != ENC_ROLE_SOLO;
|
||||
cfg.mode = phys_enc->intf_mode;
|
||||
cfg.intf = interface;
|
||||
|
||||
if (cfg.en && phys_enc->ops.needs_single_flush &&
|
||||
phys_enc->ops.needs_single_flush(phys_enc))
|
||||
cfg.split_flush_en = true;
|
||||
|
||||
topology = sde_connector_get_topology_name(phys_enc->connector);
|
||||
if (topology == SDE_RM_TOPOLOGY_PPSPLIT)
|
||||
cfg.pp_split_slave = cfg.intf;
|
||||
else
|
||||
cfg.pp_split_slave = INTF_MAX;
|
||||
|
||||
if (phys_enc->split_role != ENC_ROLE_SLAVE) {
|
||||
/* master/solo encoder */
|
||||
SDE_DEBUG_ENC(sde_enc, "enable %d\n", cfg.en);
|
||||
|
||||
if (hw_mdptop->ops.setup_split_pipe)
|
||||
hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg);
|
||||
} else {
|
||||
/*
|
||||
* slave encoder
|
||||
* - determine split index from master index,
|
||||
* assume master is first pp
|
||||
*/
|
||||
cfg.pp_split_index = sde_enc->hw_pp[0]->idx - PINGPONG_0;
|
||||
SDE_DEBUG_ENC(sde_enc, "master using pp%d\n",
|
||||
cfg.pp_split_index);
|
||||
|
||||
if (hw_mdptop->ops.setup_pp_split)
|
||||
hw_mdptop->ops.setup_pp_split(hw_mdptop, &cfg);
|
||||
}
|
||||
}
|
||||
|
||||
static int sde_encoder_virt_atomic_check(
|
||||
struct drm_encoder *drm_enc,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
|
@ -581,6 +632,7 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
|
|||
struct sde_encoder_phys *phys, uint32_t extra_flush_bits)
|
||||
{
|
||||
struct sde_hw_ctl *ctl;
|
||||
int pending_kickoff_cnt;
|
||||
|
||||
if (!drm_enc || !phys) {
|
||||
SDE_ERROR("invalid argument(s), drm_enc %d, phys_enc %d\n",
|
||||
|
@ -594,6 +646,10 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
|
|||
return;
|
||||
}
|
||||
|
||||
pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys);
|
||||
SDE_EVT32(DRMID(&to_sde_encoder_virt(drm_enc)->base),
|
||||
phys->intf_idx, pending_kickoff_cnt);
|
||||
|
||||
if (extra_flush_bits && ctl->ops.update_pending_flush)
|
||||
ctl->ops.update_pending_flush(ctl, extra_flush_bits);
|
||||
|
||||
|
@ -674,7 +730,6 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
|
|||
struct sde_hw_ctl *ctl;
|
||||
uint32_t i, pending_flush;
|
||||
unsigned long lock_flags;
|
||||
int pending_kickoff_cnt;
|
||||
|
||||
if (!sde_enc) {
|
||||
SDE_ERROR("invalid encoder\n");
|
||||
|
@ -689,18 +744,16 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
|
|||
/* don't perform flush/start operations for slave encoders */
|
||||
for (i = 0; i < sde_enc->num_phys_encs; i++) {
|
||||
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
|
||||
|
||||
if (!phys || phys->enable_state == SDE_ENC_DISABLED)
|
||||
continue;
|
||||
|
||||
pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys);
|
||||
SDE_EVT32(DRMID(&sde_enc->base), i, pending_kickoff_cnt);
|
||||
|
||||
ctl = phys->hw_ctl;
|
||||
if (!ctl)
|
||||
continue;
|
||||
|
||||
if (!phys->ops.needs_split_flush ||
|
||||
!phys->ops.needs_split_flush(phys))
|
||||
if (!phys->ops.needs_single_flush ||
|
||||
!phys->ops.needs_single_flush(phys))
|
||||
_sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0);
|
||||
else if (ctl->ops.get_pending_flush)
|
||||
pending_flush |= ctl->ops.get_pending_flush(ctl);
|
||||
|
|
|
@ -87,7 +87,7 @@ struct sde_encoder_virt_ops {
|
|||
* For CMD encoder, may wait for previous tx done
|
||||
* @handle_post_kickoff: Do any work necessary post-kickoff work
|
||||
* @trigger_start: Process start event on physical encoder
|
||||
* @needs_split_flush: Whether encoder type needs split flush
|
||||
* @needs_single_flush: Whether encoder slaves need to be flushed
|
||||
* @setup_misr: Sets up MISR, enable and disables based on sysfs
|
||||
* @collect_misr: Collects MISR data on frame update
|
||||
*/
|
||||
|
@ -114,7 +114,7 @@ struct sde_encoder_phys_ops {
|
|||
void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc);
|
||||
void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
|
||||
void (*trigger_start)(struct sde_encoder_phys *phys_enc);
|
||||
bool (*needs_split_flush)(struct sde_encoder_phys *phys_enc);
|
||||
bool (*needs_single_flush)(struct sde_encoder_phys *phys_enc);
|
||||
|
||||
void (*setup_misr)(struct sde_encoder_phys *phys_encs,
|
||||
struct sde_misr_params *misr_map);
|
||||
|
@ -388,4 +388,15 @@ static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode(
|
|||
return BLEND_3D_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* sde_encoder_helper_split_config - split display configuration helper function
|
||||
* This helper function may be used by physical encoders to configure
|
||||
* the split display related registers.
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
* @interface: enum sde_intf setting
|
||||
*/
|
||||
void sde_encoder_helper_split_config(
|
||||
struct sde_encoder_phys *phys_enc,
|
||||
enum sde_intf interface);
|
||||
|
||||
#endif /* __sde_encoder_phys_H__ */
|
||||
|
|
|
@ -80,9 +80,8 @@ static void sde_encoder_phys_cmd_mode_set(
|
|||
/* Retrieve previously allocated HW Resources. Shouldn't fail */
|
||||
sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CTL);
|
||||
for (i = 0; i <= instance; i++) {
|
||||
sde_rm_get_hw(rm, &iter);
|
||||
if (i == instance)
|
||||
phys_enc->hw_ctl = (struct sde_hw_ctl *) iter.hw;
|
||||
if (sde_rm_get_hw(rm, &iter))
|
||||
phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(phys_enc->hw_ctl)) {
|
||||
|
@ -129,6 +128,22 @@ static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
|
|||
phys_enc);
|
||||
}
|
||||
|
||||
static bool _sde_encoder_phys_is_ppsplit_slave(
|
||||
struct sde_encoder_phys *phys_enc)
|
||||
{
|
||||
enum sde_rm_topology_name topology;
|
||||
|
||||
if (!phys_enc)
|
||||
return false;
|
||||
|
||||
topology = sde_connector_get_topology_name(phys_enc->connector);
|
||||
if (topology == SDE_RM_TOPOLOGY_PPSPLIT &&
|
||||
phys_enc->split_role == ENC_ROLE_SLAVE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _sde_encoder_phys_cmd_wait_for_idle(
|
||||
struct sde_encoder_phys *phys_enc)
|
||||
{
|
||||
|
@ -137,6 +152,15 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
|
|||
u32 irq_status;
|
||||
int ret;
|
||||
|
||||
if (!phys_enc) {
|
||||
SDE_ERROR("invalid encoder\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* slave encoder doesn't enable for ppsplit */
|
||||
if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
|
||||
return 0;
|
||||
|
||||
/* return EWOULDBLOCK since we know the wait isn't necessary */
|
||||
if (phys_enc->enable_state == SDE_ENC_DISABLED) {
|
||||
SDE_ERROR_CMDENC(cmd_enc, "encoder is disabled\n");
|
||||
|
@ -374,34 +398,16 @@ static void sde_encoder_phys_cmd_pingpong_config(
|
|||
sde_encoder_phys_cmd_tearcheck_config(phys_enc);
|
||||
}
|
||||
|
||||
static bool sde_encoder_phys_cmd_needs_split_flush(
|
||||
static bool sde_encoder_phys_cmd_needs_single_flush(
|
||||
struct sde_encoder_phys *phys_enc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
enum sde_rm_topology_name topology;
|
||||
|
||||
static void sde_encoder_phys_cmd_split_config(
|
||||
struct sde_encoder_phys *phys_enc, bool enable)
|
||||
{
|
||||
struct sde_encoder_phys_cmd *cmd_enc =
|
||||
to_sde_encoder_phys_cmd(phys_enc);
|
||||
struct sde_hw_mdp *hw_mdptop = phys_enc->hw_mdptop;
|
||||
struct split_pipe_cfg cfg = { 0 };
|
||||
if (!phys_enc)
|
||||
return false;
|
||||
|
||||
if (!phys_enc) {
|
||||
SDE_ERROR("invalid encoder\n");
|
||||
return;
|
||||
}
|
||||
SDE_DEBUG_CMDENC(cmd_enc, "enable %d\n", enable);
|
||||
|
||||
cfg.en = enable;
|
||||
cfg.mode = INTF_MODE_CMD;
|
||||
cfg.intf = cmd_enc->intf_idx;
|
||||
cfg.split_flush_en = enable &&
|
||||
sde_encoder_phys_cmd_needs_split_flush(phys_enc);
|
||||
|
||||
if (hw_mdptop && hw_mdptop->ops.setup_split_pipe)
|
||||
hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg);
|
||||
topology = sde_connector_get_topology_name(phys_enc->connector);
|
||||
return topology == SDE_RM_TOPOLOGY_PPSPLIT;
|
||||
}
|
||||
|
||||
static int sde_encoder_phys_cmd_control_vblank_irq(
|
||||
|
@ -453,28 +459,26 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc)
|
|||
to_sde_encoder_phys_cmd(phys_enc);
|
||||
struct sde_hw_ctl *ctl;
|
||||
u32 flush_mask;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!phys_enc) {
|
||||
SDE_ERROR("invalid encoder\n");
|
||||
if (!phys_enc || !phys_enc->hw_ctl) {
|
||||
SDE_ERROR("invalid arg(s), encoder %d\n", phys_enc != 0);
|
||||
return;
|
||||
}
|
||||
SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);
|
||||
|
||||
if (WARN_ON(phys_enc->enable_state == SDE_ENC_ENABLED))
|
||||
if (phys_enc->enable_state == SDE_ENC_ENABLED) {
|
||||
SDE_ERROR("already enabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only master configures master/slave configuration, so no slave check
|
||||
* In solo configuration, solo encoder needs to program no-split
|
||||
*/
|
||||
if (phys_enc->split_role == ENC_ROLE_MASTER)
|
||||
sde_encoder_phys_cmd_split_config(phys_enc, true);
|
||||
else if (phys_enc->split_role == ENC_ROLE_SOLO)
|
||||
sde_encoder_phys_cmd_split_config(phys_enc, false);
|
||||
sde_encoder_helper_split_config(phys_enc, cmd_enc->intf_idx);
|
||||
|
||||
sde_encoder_phys_cmd_pingpong_config(phys_enc);
|
||||
|
||||
if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
|
||||
goto update_flush;
|
||||
|
||||
/* Both master and slave need to register for pp_tx_done */
|
||||
ret = sde_encoder_phys_cmd_register_irq(phys_enc,
|
||||
SDE_IRQ_TYPE_PING_PONG_COMP,
|
||||
|
@ -503,6 +507,7 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc)
|
|||
return;
|
||||
}
|
||||
|
||||
update_flush:
|
||||
ctl = phys_enc->hw_ctl;
|
||||
ctl->ops.get_bitmask_intf(ctl, &flush_mask, cmd_enc->intf_idx);
|
||||
ctl->ops.update_pending_flush(ctl, flush_mask);
|
||||
|
@ -524,24 +529,30 @@ static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc)
|
|||
}
|
||||
SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);
|
||||
|
||||
if (WARN_ON(phys_enc->enable_state == SDE_ENC_DISABLED))
|
||||
if (phys_enc->enable_state == SDE_ENC_DISABLED) {
|
||||
SDE_ERROR_CMDENC(cmd_enc, "already disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0);
|
||||
|
||||
ret = _sde_encoder_phys_cmd_wait_for_idle(phys_enc);
|
||||
if (ret) {
|
||||
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
||||
SDE_ERROR_CMDENC(cmd_enc,
|
||||
"pp %d failed wait for idle at disable: %d\n",
|
||||
phys_enc->hw_pp->idx - PINGPONG_0, ret);
|
||||
SDE_EVT32(DRMID(phys_enc->parent),
|
||||
phys_enc->hw_pp->idx - PINGPONG_0, ret);
|
||||
}
|
||||
if (!_sde_encoder_phys_is_ppsplit_slave(phys_enc)) {
|
||||
ret = _sde_encoder_phys_cmd_wait_for_idle(phys_enc);
|
||||
if (ret) {
|
||||
atomic_set(&phys_enc->pending_kickoff_cnt, 0);
|
||||
SDE_ERROR_CMDENC(cmd_enc,
|
||||
"pp %d failed wait for idle, %d\n",
|
||||
phys_enc->hw_pp->idx - PINGPONG_0, ret);
|
||||
SDE_EVT32(DRMID(phys_enc->parent),
|
||||
phys_enc->hw_pp->idx - PINGPONG_0, ret);
|
||||
}
|
||||
|
||||
sde_encoder_phys_cmd_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
|
||||
sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false);
|
||||
sde_encoder_phys_cmd_unregister_irq(phys_enc, INTR_IDX_PINGPONG);
|
||||
sde_encoder_phys_cmd_unregister_irq(
|
||||
phys_enc, INTR_IDX_UNDERRUN);
|
||||
sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false);
|
||||
sde_encoder_phys_cmd_unregister_irq(
|
||||
phys_enc, INTR_IDX_PINGPONG);
|
||||
}
|
||||
|
||||
phys_enc->enable_state = SDE_ENC_DISABLED;
|
||||
|
||||
|
@ -634,7 +645,7 @@ static void sde_encoder_phys_cmd_init_ops(
|
|||
ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done;
|
||||
ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
|
||||
ops->trigger_start = sde_encoder_helper_trigger_start;
|
||||
ops->needs_split_flush = sde_encoder_phys_cmd_needs_split_flush;
|
||||
ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
|
||||
}
|
||||
|
||||
struct sde_encoder_phys *sde_encoder_phys_cmd_init(
|
||||
|
|
|
@ -319,38 +319,12 @@ static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx)
|
|||
phys_enc);
|
||||
}
|
||||
|
||||
static bool sde_encoder_phys_vid_needs_split_flush(
|
||||
static bool sde_encoder_phys_vid_needs_single_flush(
|
||||
struct sde_encoder_phys *phys_enc)
|
||||
{
|
||||
return phys_enc && phys_enc->split_role != ENC_ROLE_SOLO;
|
||||
}
|
||||
|
||||
static void _sde_encoder_phys_vid_split_config(
|
||||
struct sde_encoder_phys *phys_enc, bool enable)
|
||||
{
|
||||
struct sde_encoder_phys_vid *vid_enc =
|
||||
to_sde_encoder_phys_vid(phys_enc);
|
||||
struct sde_hw_mdp *hw_mdptop = phys_enc->hw_mdptop;
|
||||
struct split_pipe_cfg cfg = { 0 };
|
||||
|
||||
SDE_DEBUG_VIDENC(vid_enc, "enable %d\n", enable);
|
||||
|
||||
cfg.en = enable;
|
||||
cfg.mode = INTF_MODE_VIDEO;
|
||||
cfg.intf = vid_enc->hw_intf->idx;
|
||||
cfg.split_flush_en = enable &&
|
||||
sde_encoder_phys_vid_needs_split_flush(phys_enc);
|
||||
|
||||
/* Configure split pipe control to handle master/slave triggering */
|
||||
if (hw_mdptop && hw_mdptop->ops.setup_split_pipe) {
|
||||
unsigned long lock_flags;
|
||||
|
||||
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
|
||||
hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg);
|
||||
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
|
||||
}
|
||||
}
|
||||
|
||||
static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc,
|
||||
enum sde_intr_type intr_type, int idx,
|
||||
void (*irq_func)(void *, int), const char *irq_name)
|
||||
|
@ -429,16 +403,17 @@ static void sde_encoder_phys_vid_mode_set(
|
|||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
{
|
||||
struct sde_rm *rm = &phys_enc->sde_kms->rm;
|
||||
struct sde_rm *rm;
|
||||
struct sde_rm_hw_iter iter;
|
||||
int i, instance;
|
||||
struct sde_encoder_phys_vid *vid_enc;
|
||||
|
||||
if (!phys_enc) {
|
||||
if (!phys_enc || !phys_enc->sde_kms) {
|
||||
SDE_ERROR("invalid encoder\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rm = &phys_enc->sde_kms->rm;
|
||||
vid_enc = to_sde_encoder_phys_vid(phys_enc);
|
||||
phys_enc->cached_mode = *adj_mode;
|
||||
SDE_DEBUG_VIDENC(vid_enc, "caching mode:\n");
|
||||
|
@ -449,11 +424,9 @@ static void sde_encoder_phys_vid_mode_set(
|
|||
/* Retrieve previously allocated HW Resources. Shouldn't fail */
|
||||
sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CTL);
|
||||
for (i = 0; i <= instance; i++) {
|
||||
sde_rm_get_hw(rm, &iter);
|
||||
if (i == instance)
|
||||
phys_enc->hw_ctl = (struct sde_hw_ctl *) iter.hw;
|
||||
if (sde_rm_get_hw(rm, &iter))
|
||||
phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(phys_enc->hw_ctl)) {
|
||||
SDE_ERROR_VIDENC(vid_enc, "failed to init ctl, %ld\n",
|
||||
PTR_ERR(phys_enc->hw_ctl));
|
||||
|
@ -531,10 +504,7 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
|
|||
if (WARN_ON(!vid_enc->hw_intf->ops.enable_timing))
|
||||
return;
|
||||
|
||||
if (phys_enc->split_role == ENC_ROLE_MASTER)
|
||||
_sde_encoder_phys_vid_split_config(phys_enc, true);
|
||||
else if (phys_enc->split_role == ENC_ROLE_SOLO)
|
||||
_sde_encoder_phys_vid_split_config(phys_enc, false);
|
||||
sde_encoder_helper_split_config(phys_enc, vid_enc->hw_intf->idx);
|
||||
|
||||
sde_encoder_phys_vid_setup_timing_engine(phys_enc);
|
||||
ret = sde_encoder_phys_vid_control_vblank_irq(phys_enc, true);
|
||||
|
@ -772,7 +742,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
|
|||
ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq;
|
||||
ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done;
|
||||
ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff;
|
||||
ops->needs_split_flush = sde_encoder_phys_vid_needs_split_flush;
|
||||
ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush;
|
||||
ops->setup_misr = sde_encoder_phys_vid_setup_misr;
|
||||
ops->collect_misr = sde_encoder_phys_vid_collect_misr;
|
||||
}
|
||||
|
|
|
@ -155,6 +155,7 @@ enum {
|
|||
TE2_LEN,
|
||||
DSC_OFF,
|
||||
DSC_LEN,
|
||||
PP_SLAVE,
|
||||
PP_PROP_MAX,
|
||||
};
|
||||
|
||||
|
@ -347,6 +348,7 @@ static struct sde_prop_type pp_prop[] = {
|
|||
{TE2_LEN, "qcom,sde-te2-size", false, PROP_TYPE_U32},
|
||||
{DSC_OFF, "qcom,sde-dsc-off", false, PROP_TYPE_U32_ARRAY},
|
||||
{DSC_LEN, "qcom,sde-dsc-size", false, PROP_TYPE_U32},
|
||||
{PP_SLAVE, "qcom,sde-pp-slave", false, PROP_TYPE_U32_ARRAY},
|
||||
};
|
||||
|
||||
static struct sde_prop_type cdm_prop[] = {
|
||||
|
@ -1723,6 +1725,9 @@ static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
|
|||
set_bit(SDE_PINGPONG_SPLIT, &pp->features);
|
||||
}
|
||||
|
||||
if (PROP_VALUE_ACCESS(prop_value, PP_SLAVE, i))
|
||||
set_bit(SDE_PINGPONG_SLAVE, &pp->features);
|
||||
|
||||
sblk->dsc.base = PROP_VALUE_ACCESS(prop_value, DSC_OFF, i);
|
||||
if (sblk->dsc.base) {
|
||||
sblk->dsc.id = SDE_PINGPONG_DSC;
|
||||
|
|
|
@ -159,6 +159,7 @@ enum {
|
|||
* @SDE_PINGPONG_TE Tear check block
|
||||
* @SDE_PINGPONG_TE2 Additional tear check block for split pipes
|
||||
* @SDE_PINGPONG_SPLIT PP block supports split fifo
|
||||
* @SDE_PINGPONG_SLAVE PP block is a suitable slave for split fifo
|
||||
* @SDE_PINGPONG_DSC, Display stream compression blocks
|
||||
* @SDE_PINGPONG_MAX
|
||||
*/
|
||||
|
@ -166,6 +167,7 @@ enum {
|
|||
SDE_PINGPONG_TE = 0x1,
|
||||
SDE_PINGPONG_TE2,
|
||||
SDE_PINGPONG_SPLIT,
|
||||
SDE_PINGPONG_SLAVE,
|
||||
SDE_PINGPONG_DSC,
|
||||
SDE_PINGPONG_MAX
|
||||
};
|
||||
|
|
|
@ -29,13 +29,16 @@
|
|||
#define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4))
|
||||
#define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4
|
||||
|
||||
static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp,
|
||||
static void sde_hw_setup_split_pipe(struct sde_hw_mdp *mdp,
|
||||
struct split_pipe_cfg *cfg)
|
||||
{
|
||||
struct sde_hw_blk_reg_map *c = &mdp->hw;
|
||||
u32 upper_pipe = 0;
|
||||
u32 lower_pipe = 0;
|
||||
|
||||
if (!mdp || !cfg)
|
||||
return;
|
||||
|
||||
if (cfg->en) {
|
||||
if (cfg->mode == INTF_MODE_CMD) {
|
||||
lower_pipe = FLD_SPLIT_DISPLAY_CMD;
|
||||
|
@ -46,7 +49,7 @@ static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp,
|
|||
lower_pipe |= FLD_INTF_2_SW_TRG_MUX;
|
||||
|
||||
/* free run */
|
||||
if (cfg->pp_split)
|
||||
if (cfg->pp_split_slave != INTF_MAX)
|
||||
lower_pipe = FLD_SMART_PANEL_FREE_RUN;
|
||||
|
||||
upper_pipe = lower_pipe;
|
||||
|
@ -61,12 +64,39 @@ static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp,
|
|||
}
|
||||
}
|
||||
|
||||
SDE_REG_WRITE(c, SSPP_SPARE, (cfg->split_flush_en) ? 0x1 : 0x0);
|
||||
SDE_REG_WRITE(c, SSPP_SPARE, cfg->split_flush_en ? 0x1 : 0x0);
|
||||
SDE_REG_WRITE(c, SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower_pipe);
|
||||
SDE_REG_WRITE(c, SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper_pipe);
|
||||
SDE_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1);
|
||||
}
|
||||
|
||||
static void sde_hw_setup_pp_split(struct sde_hw_mdp *mdp,
|
||||
struct split_pipe_cfg *cfg)
|
||||
{
|
||||
u32 ppb_config = 0x0;
|
||||
u32 ppb_control = 0x0;
|
||||
|
||||
if (!mdp || !cfg)
|
||||
return;
|
||||
|
||||
if (cfg->en && cfg->pp_split_slave != INTF_MAX) {
|
||||
ppb_config |= (cfg->pp_split_slave - INTF_0 + 1) << 20;
|
||||
ppb_config |= BIT(16); /* split enable */
|
||||
ppb_control = BIT(5); /* horz split*/
|
||||
}
|
||||
if (cfg->pp_split_index) {
|
||||
SDE_REG_WRITE(&mdp->hw, PPB0_CONFIG, 0x0);
|
||||
SDE_REG_WRITE(&mdp->hw, PPB0_CNTL, 0x0);
|
||||
SDE_REG_WRITE(&mdp->hw, PPB1_CONFIG, ppb_config);
|
||||
SDE_REG_WRITE(&mdp->hw, PPB1_CNTL, ppb_control);
|
||||
} else {
|
||||
SDE_REG_WRITE(&mdp->hw, PPB0_CONFIG, ppb_config);
|
||||
SDE_REG_WRITE(&mdp->hw, PPB0_CNTL, ppb_control);
|
||||
SDE_REG_WRITE(&mdp->hw, PPB1_CONFIG, 0x0);
|
||||
SDE_REG_WRITE(&mdp->hw, PPB1_CNTL, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
static void sde_hw_setup_cdm_output(struct sde_hw_mdp *mdp,
|
||||
struct cdm_output_cfg *cfg)
|
||||
{
|
||||
|
@ -112,7 +142,8 @@ static bool sde_hw_setup_clk_force_ctrl(struct sde_hw_mdp *mdp,
|
|||
static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
|
||||
unsigned long cap)
|
||||
{
|
||||
ops->setup_split_pipe = sde_hw_setup_split_pipe_control;
|
||||
ops->setup_split_pipe = sde_hw_setup_split_pipe;
|
||||
ops->setup_pp_split = sde_hw_setup_pp_split;
|
||||
ops->setup_cdm_output = sde_hw_setup_cdm_output;
|
||||
ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ struct traffic_shaper_cfg {
|
|||
* @en : Enable/disable dual pipe confguration
|
||||
* @mode : Panel interface mode
|
||||
* @intf : Interface id for main control path
|
||||
* @pp_split : Ping pong split is enabled or disabled
|
||||
* @pp_split_slave: Slave interface for ping pong split, INTF_MAX to disable
|
||||
* @pp_split_idx: Ping pong index for ping pong split
|
||||
* @split_flush_en: Allows both the paths to be flushed when master path is
|
||||
* flushed
|
||||
*/
|
||||
|
@ -48,7 +49,8 @@ struct split_pipe_cfg {
|
|||
bool en;
|
||||
enum sde_intf_mode mode;
|
||||
enum sde_intf intf;
|
||||
bool pp_split;
|
||||
enum sde_intf pp_split_slave;
|
||||
u32 pp_split_index;
|
||||
bool split_flush_en;
|
||||
};
|
||||
|
||||
|
@ -66,6 +68,7 @@ struct cdm_output_cfg {
|
|||
* struct sde_hw_mdp_ops - interface to the MDP TOP Hw driver functions
|
||||
* Assumption is these functions will be called after clocks are enabled.
|
||||
* @setup_split_pipe : Programs the pipe control registers
|
||||
* @setup_pp_split : Programs the pp split control registers
|
||||
* @setup_cdm_output : programs cdm control
|
||||
* @setup_traffic_shaper : programs traffic shaper control
|
||||
*/
|
||||
|
@ -78,6 +81,13 @@ struct sde_hw_mdp_ops {
|
|||
void (*setup_split_pipe)(struct sde_hw_mdp *mdp,
|
||||
struct split_pipe_cfg *p);
|
||||
|
||||
/** setup_pp_split() : Configure pp split related registers
|
||||
* @mdp : mdp top context driver
|
||||
* @cfg : upper and lower part of pipe configuration
|
||||
*/
|
||||
void (*setup_pp_split)(struct sde_hw_mdp *mdp,
|
||||
struct split_pipe_cfg *cfg);
|
||||
|
||||
/**
|
||||
* setup_cdm_output() : Setup selection control of the cdm data path
|
||||
* @mdp : mdp top context driver
|
||||
|
|
|
@ -592,7 +592,7 @@ static int _sde_rm_reserve_lms(
|
|||
struct sde_rm_hw_blk *pp[MAX_BLOCKS];
|
||||
struct sde_rm_hw_iter iter_i, iter_j;
|
||||
int lm_count = 0;
|
||||
int i;
|
||||
int i, rc = 0;
|
||||
|
||||
if (!reqs->num_lm) {
|
||||
SDE_ERROR("invalid number of lm: %d\n", reqs->num_lm);
|
||||
|
@ -651,7 +651,27 @@ static int _sde_rm_reserve_lms(
|
|||
dspp[i] ? dspp[i]->id : 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (reqs->top_name == SDE_RM_TOPOLOGY_PPSPLIT) {
|
||||
/* reserve a free PINGPONG_SLAVE block */
|
||||
rc = -ENAVAIL;
|
||||
sde_rm_init_hw_iter(&iter_i, 0, SDE_HW_BLK_PINGPONG);
|
||||
while (sde_rm_get_hw(rm, &iter_i)) {
|
||||
struct sde_pingpong_cfg *pp_cfg =
|
||||
(struct sde_pingpong_cfg *)
|
||||
(iter_i.blk->catalog);
|
||||
|
||||
if (!(test_bit(SDE_PINGPONG_SLAVE, &pp_cfg->features)))
|
||||
continue;
|
||||
if (RESERVED_BY_OTHER(iter_i.blk, rsvp))
|
||||
continue;
|
||||
|
||||
iter_i.blk->rsvp_nxt = rsvp;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _sde_rm_reserve_ctls(
|
||||
|
|
Loading…
Add table
Reference in a new issue