summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt3
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c3
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c212
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h5
-rw-r--r--drivers/platform/msm/gsi/gsi.c29
-rw-r--r--drivers/video/fbdev/msm/mdss.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c11
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h12
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c20
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c102
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c71
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.c1
-rw-r--r--include/uapi/linux/msm_mdp_ext.h9
-rw-r--r--sound/soc/codecs/wcd9335.c106
-rw-r--r--sound/soc/codecs/wcd9335.h2
-rw-r--r--sound/soc/msm/msm8996.c14
19 files changed, 518 insertions, 94 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 9fc942cc627d..90abf0305319 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -152,7 +152,8 @@ Optional properties:
"dfps_immediate_porch_mode_vfp" = FPS change request is
implemented immediately by changing panel vertical
front porch values.
-- qcom,min-refresh-rate: Minimum refresh rate supported by the panel.
+- qcom,min-refresh-rate: Minimum refresh rate supported by the panel. Used in
+ adaptive variable refresh(AVR) to compute new avr vtotal
- qcom,max-refresh-rate: Maximum refresh rate supported by the panel. If max refresh
rate is not specified, then the frame rate of the panel in
qcom,mdss-dsi-panel-framerate is used.
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
index 6b43efce453f..03a61407aef8 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -788,8 +788,9 @@ static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
rc = msm_dma_map_sg_lazy(iommu_cb_set.cb_info[idx].dev, table->sgl,
table->nents, dma_dir, buf);
- if (!rc) {
+ if (rc != table->nents) {
pr_err("Error: msm_dma_map_sg_lazy failed\n");
+ rc = -ENOMEM;
goto err_unmap_sg;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index c141797bcd3c..c8d14a6d253b 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -77,6 +77,142 @@
#define SDE_ROTREG_READ(base, off) \
readl_relaxed(base + (off))
+/* Invalid software timestamp value for initialization */
+#define SDE_REGDMA_SWTS_INVALID (~0)
+
+/**
+ * sde_hw_rotator_elapsed_swts - Find difference of 2 software timestamps
+ * @ts_curr: current software timestamp
+ * @ts_prev: previous software timestamp
+ * @return: the amount ts_curr is ahead of ts_prev
+ */
+static int sde_hw_rotator_elapsed_swts(u32 ts_curr, u32 ts_prev)
+{
+ u32 diff = (ts_curr - ts_prev) & SDE_REGDMA_SWTS_MASK;
+
+ return sign_extend32(diff, (SDE_REGDMA_SWTS_SHIFT - 1));
+}
+
+/**
+ * sde_hw_rotator_pending_swts - Check if the given context is still pending
+ * @rot: Pointer to hw rotator
+ * @ctx: Pointer to rotator context
+ * @pswts: Pointer to returned reference software timestamp, optional
+ * @return: true if context has pending requests
+ */
+static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
+ struct sde_hw_rotator_context *ctx, u32 *pswts)
+{
+ u32 swts;
+ int ts_diff;
+ bool pending;
+
+ if (ctx->last_regdma_timestamp == SDE_REGDMA_SWTS_INVALID)
+ swts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+ else
+ swts = ctx->last_regdma_timestamp;
+
+ if (ctx->q_id == ROT_QUEUE_LOW_PRIORITY)
+ swts >>= SDE_REGDMA_SWTS_SHIFT;
+
+ swts &= SDE_REGDMA_SWTS_MASK;
+
+ ts_diff = sde_hw_rotator_elapsed_swts(ctx->timestamp, swts);
+
+ if (pswts)
+ *pswts = swts;
+
+ pending = (ts_diff > 0) ? true : false;
+
+ SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
+ ctx->timestamp, ctx->q_id, swts, pending);
+ return pending;
+}
+
+/**
+ * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
+ * Also, clear rotator/regdma irq status.
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
+{
+ SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
+ atomic_read(&rot->irq_enabled));
+
+ if (!atomic_read(&rot->irq_enabled)) {
+ if (rot->mode == ROT_REGDMA_OFF)
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
+ ROT_DONE_MASK);
+ else
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);
+
+ enable_irq(rot->irq_num);
+ }
+ atomic_inc(&rot->irq_enabled);
+}
+
+/**
+ * sde_hw_rotator_disable_irq - Disable hw rotator interrupt with ref. count
+ * Also, clear rotator/regdma irq enable masks.
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_disable_irq(struct sde_hw_rotator *rot)
+{
+ SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
+ atomic_read(&rot->irq_enabled));
+
+ if (!atomic_read(&rot->irq_enabled)) {
+ SDEROT_ERR("irq %d is already disabled\n", rot->irq_num);
+ return;
+ }
+
+ if (!atomic_dec_return(&rot->irq_enabled)) {
+ if (rot->mode == ROT_REGDMA_OFF)
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0);
+ else
+ SDE_ROTREG_WRITE(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_EN, 0);
+ /* disable irq after last pending irq is handled, if any */
+ synchronize_irq(rot->irq_num);
+ disable_irq_nosync(rot->irq_num);
+ }
+}
+
+/**
+ * sde_hw_rotator_dump_status - Dump hw rotator status on error
+ * @rot: Pointer to hw rotator
+ */
+static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
+{
+ SDEROT_ERR(
+ "op_mode = %x, int_en = %x, int_status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_OP_MODE),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_EN),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INT_STATUS));
+
+ SDEROT_ERR(
+ "ts = %x, q0_status = %x, q1_status = %x, block_status = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_TIMESTAMP_REG),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_0_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_QUEUE_1_STATUS),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_BLOCK_STATUS));
+
+ SDEROT_ERR(
+ "invalid_cmd_offset = %x, fsm_state = %x\n",
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET),
+ SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_FSM_STATE));
+}
+
/**
* sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
* on provided session_id. Each rotator has a different session_id.
@@ -476,7 +612,7 @@ static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
reinit_completion(&ctx->rot_comp);
- enable_irq(rot->irq_num);
+ sde_hw_rotator_enable_irq(rot);
}
SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
@@ -572,9 +708,6 @@ static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx,
wrptr = sde_hw_rotator_get_regdma_segment(ctx);
- if (rot->irq_num >= 0)
- reinit_completion(&ctx->regdma_comp);
-
/*
* Last ROT command must be ROT_START before REGDMA start
*/
@@ -676,7 +809,7 @@ static u32 sde_hw_rotator_wait_done_no_regdma(
SDEROT_WARN(
"Timeout waiting, but rotator job is done!!\n");
- disable_irq_nosync(rot->irq_num);
+ sde_hw_rotator_disable_irq(rot);
}
spin_unlock_irqrestore(&rot->rotisr_lock, flags);
} else {
@@ -719,13 +852,15 @@ static u32 sde_hw_rotator_wait_done_regdma(
u32 last_isr;
u32 last_ts;
u32 int_id;
+ u32 swts;
u32 sts = 0;
unsigned long flags;
if (rot->irq_num >= 0) {
SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
ctx, ctx->timestamp);
- rc = wait_for_completion_timeout(&ctx->regdma_comp,
+ rc = wait_event_timeout(ctx->regdma_waitq,
+ !sde_hw_rotator_pending_swts(rot, ctx, &swts),
KOFF_TIMEOUT);
spin_lock_irqsave(&rot->rotisr_lock, flags);
@@ -738,11 +873,12 @@ static u32 sde_hw_rotator_wait_done_regdma(
status, int_id, last_ts);
if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
+ bool pending;
+
+ pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
SDEROT_ERR(
- "Timeout wait for regdma interrupt status, ts:%X\n",
- ctx->timestamp);
- SDEROT_ERR("last_isr:0x%X, last_ts:0x%X, rc=%d\n",
- last_isr, last_ts, rc);
+ "Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
+ ctx->timestamp, swts, pending);
if (status & REGDMA_WATCHDOG_INT)
SDEROT_ERR("REGDMA watchdog interrupt\n");
@@ -753,24 +889,13 @@ static u32 sde_hw_rotator_wait_done_regdma(
else if (status & REGDMA_INVALID_CMD)
SDEROT_ERR("REGDMA invalid command\n");
+ sde_hw_rotator_dump_status(rot);
status = ROT_ERROR_BIT;
- } else if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
- /* Got to match exactly with interrupt ID */
- int_id = REGDMA_QUEUE0_INT0 << int_id;
-
- SDE_ROTREG_WRITE(rot->mdss_base,
- REGDMA_CSR_REGDMA_INT_CLEAR,
- int_id);
-
- status = 0;
- } else if (queue_id == ROT_QUEUE_LOW_PRIORITY) {
- /* Matching interrupt ID */
- int_id = REGDMA_QUEUE1_INT0 << int_id;
-
- SDE_ROTREG_WRITE(rot->mdss_base,
- REGDMA_CSR_REGDMA_INT_CLEAR,
- int_id);
-
+ } else {
+ if (rc == 1)
+ SDEROT_WARN(
+ "REGDMA done but no irq, ts:0x%X/0x%X\n",
+ ctx->timestamp, swts);
status = 0;
}
@@ -1007,7 +1132,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
}
if (resinfo->rot->irq_num >= 0)
- enable_irq(resinfo->rot->irq_num);
+ sde_hw_rotator_enable_irq(resinfo->rot);
SDEROT_DBG("New rotator resource:%p, priority:%d\n",
resinfo, wb_id);
@@ -1036,7 +1161,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
hw->pending_count);
if (resinfo->rot->irq_num >= 0)
- disable_irq(resinfo->rot->irq_num);
+ sde_hw_rotator_disable_irq(resinfo->rot);
devm_kfree(&mgr->pdev->dev, resinfo);
}
@@ -1078,8 +1203,10 @@ static struct sde_hw_rotator_context *sde_hw_rotator_alloc_rotctx(
ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
sde_hw_rotator_get_regdma_ctxidx(ctx));
+ ctx->last_regdma_timestamp = SDE_REGDMA_SWTS_INVALID;
+
init_completion(&ctx->rot_comp);
- init_completion(&ctx->regdma_comp);
+ init_waitqueue_head(&ctx->regdma_waitq);
/* Store rotator context for lookup purpose */
sde_hw_rotator_put_ctx(ctx);
@@ -1419,7 +1546,7 @@ static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
if (isr & ROT_DONE_MASK) {
if (rot->irq_num >= 0)
- disable_irq_nosync(rot->irq_num);
+ sde_hw_rotator_disable_irq(rot);
SDEROT_DBG("Notify rotator complete\n");
/* Normal rotator only 1 session, no need to lookup */
@@ -1456,6 +1583,8 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
u32 q_id;
isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS);
+ /* acknowledge interrupt before reading latest timestamp */
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR, isr);
ts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts);
@@ -1480,30 +1609,23 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
}
ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
- WARN_ON(ctx == NULL);
/*
* Wake up all waiting context from the current and previous
* SW Timestamp.
*/
- do {
+ while (ctx &&
+ sde_hw_rotator_elapsed_swts(ctx->timestamp, ts) >= 0) {
ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts;
SDEROT_DBG(
"regdma complete: ctx:%p, ts:%X\n", ctx, ts);
- complete_all(&ctx->regdma_comp);
+ wake_up_all(&ctx->regdma_waitq);
ts = (ts - 1) & SDE_REGDMA_SWTS_MASK;
ctx = rot->rotCtx[q_id]
[ts & SDE_HW_ROT_REGDMA_SEG_MASK];
- } while (ctx && (ctx->last_regdma_timestamp == 0));
-
- /*
- * Clear corresponding regdma interrupt because it is a level
- * interrupt
- */
- SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
- isr);
+ };
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
@@ -1526,16 +1648,13 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
if (ctx && ctx->last_regdma_isr_status == 0) {
ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts;
- complete_all(&ctx->regdma_comp);
+ wake_up_all(&ctx->regdma_waitq);
SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
i, j, ctx);
}
}
}
- SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
- isr);
-
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
}
@@ -1810,6 +1929,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
disable_irq(rot->irq_num);
}
}
+ atomic_set(&rot->irq_enabled, 0);
setup_rotator_ops(&rot->ops, rot->mode);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
index 610caf16c764..272b15e01e8b 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -202,7 +202,7 @@ struct sde_hw_rotator_context {
u32 *regdma_wrptr;
u32 timestamp;
struct completion rot_comp;
- struct completion regdma_comp;
+ wait_queue_head_t regdma_waitq;
struct sde_dbg_buf src_dbgbuf;
struct sde_dbg_buf dst_dbgbuf;
u32 last_regdma_isr_status;
@@ -253,6 +253,7 @@ struct sde_hw_rotator {
/* logical interrupt number */
int irq_num;
+ atomic_t irq_enabled;
/* internal ION memory for SW timestamp */
struct ion_client *iclient;
@@ -260,8 +261,6 @@ struct sde_hw_rotator {
void *swts_buffer;
u32 highest_bank;
- struct completion rot_comp;
- struct completion regdma_comp;
spinlock_t rotctx_lock;
spinlock_t rotisr_lock;
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 0069c07474d4..d2e31c3b0945 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -93,26 +93,6 @@ static void __gsi_config_gen_irq(int ee, uint32_t mask, uint32_t val)
GSI_EE_n_CNTXT_GSI_IRQ_EN_OFFS(ee));
}
-static void __gsi_config_inter_ee_ch_irq(int ee, uint32_t mask, uint32_t val)
-{
- uint32_t curr;
-
- curr = gsi_readl(gsi_ctx->base +
- GSI_INTER_EE_n_SRC_GSI_CH_IRQ_MSK_OFFS(ee));
- gsi_writel((curr & ~mask) | (val & mask), gsi_ctx->base +
- GSI_INTER_EE_n_SRC_GSI_CH_IRQ_MSK_OFFS(ee));
-}
-
-static void __gsi_config_inter_ee_evt_irq(int ee, uint32_t mask, uint32_t val)
-{
- uint32_t curr;
-
- curr = gsi_readl(gsi_ctx->base +
- GSI_INTER_EE_n_SRC_EV_CH_IRQ_MSK_OFFS(ee));
- gsi_writel((curr & ~mask) | (val & mask), gsi_ctx->base +
- GSI_INTER_EE_n_SRC_EV_CH_IRQ_MSK_OFFS(ee));
-}
-
static void gsi_handle_ch_ctrl(int ee)
{
uint32_t ch;
@@ -684,7 +664,10 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
/* only support 16 un-reserved + 7 reserved event virtual IDs */
gsi_ctx->evt_bmap = ~0x7E03FF;
- /* enable all interrupts but GSI_BREAK_POINT */
+ /*
+ * enable all interrupts but GSI_BREAK_POINT.
+ * Inter EE commands / interrupt are no supported.
+ */
__gsi_config_type_irq(props->ee, ~0, ~0);
__gsi_config_ch_irq(props->ee, ~0, ~0);
__gsi_config_evt_irq(props->ee, ~0, ~0);
@@ -692,8 +675,6 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
__gsi_config_glob_irq(props->ee, ~0, ~0);
__gsi_config_gen_irq(props->ee, ~0,
~GSI_EE_n_CNTXT_GSI_IRQ_CLR_GSI_BREAK_POINT_BMSK);
- __gsi_config_inter_ee_ch_irq(props->ee, ~0, ~0);
- __gsi_config_inter_ee_evt_irq(props->ee, ~0, ~0);
gsi_writel(props->intr, gsi_ctx->base +
GSI_EE_n_CNTXT_INTSET_OFFS(gsi_ctx->per.ee));
@@ -791,8 +772,6 @@ int gsi_deregister_device(unsigned long dev_hdl, bool force)
__gsi_config_ieob_irq(gsi_ctx->per.ee, ~0, 0);
__gsi_config_glob_irq(gsi_ctx->per.ee, ~0, 0);
__gsi_config_gen_irq(gsi_ctx->per.ee, ~0, 0);
- __gsi_config_inter_ee_ch_irq(gsi_ctx->per.ee, ~0, 0);
- __gsi_config_inter_ee_evt_irq(gsi_ctx->per.ee, ~0, 0);
devm_free_irq(gsi_ctx->dev, gsi_ctx->per.irq, gsi_ctx);
devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 88950e9cb2aa..b601cafba6fd 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -176,6 +176,7 @@ enum mdss_hw_capabilities {
MDSS_CAPS_10_BIT_SUPPORTED,
MDSS_CAPS_CWB_SUPPORTED,
MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
+ MDSS_CAPS_AVR_SUPPORTED,
MDSS_CAPS_MAX,
};
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 9548ea471385..2e54f335e948 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -1967,7 +1967,6 @@ static void mdss_dsi_parse_dfps_config(struct device_node *pan_node,
pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE;
pr_debug("default dfps mode: suspend/resume\n");
}
- mdss_dsi_set_refresh_rate_range(pan_node, pinfo);
} else {
pinfo->dynamic_fps = false;
pr_debug("dfps update mode not configured: disable\n");
@@ -2528,6 +2527,8 @@ static int mdss_panel_parse_dt(struct device_node *np,
mdss_dsi_parse_dfps_config(np, ctrl_pdata);
+ mdss_dsi_set_refresh_rate_range(np, pinfo);
+
pinfo->is_dba_panel = of_property_read_bool(np,
"qcom,dba-panel");
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index cc8ce49c7387..99a924c38d65 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -552,13 +552,19 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,
struct msm_fb_data_type *mfd = fbi->par;
struct mdss_panel_info *pinfo = mfd->panel_info;
int ret;
+ bool dfps_porch_mode = false;
+
+ if (pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP ||
+ pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP)
+ dfps_porch_mode = true;
ret = scnprintf(buf, PAGE_SIZE,
"pu_en=%d\nxstart=%d\nwalign=%d\nystart=%d\nhalign=%d\n"
"min_w=%d\nmin_h=%d\nroi_merge=%d\ndyn_fps_en=%d\n"
"min_fps=%d\nmax_fps=%d\npanel_name=%s\n"
"primary_panel=%d\nis_pluggable=%d\ndisplay_id=%s\n"
- "is_cec_supported=%d\nis_pingpong_split=%d\n",
+ "is_cec_supported=%d\nis_pingpong_split=%d\n"
+ "dfps_porch_mode=%d\n",
pinfo->partial_update_enabled,
pinfo->roi_alignment.xstart_pix_align,
pinfo->roi_alignment.width_pix_align,
@@ -570,7 +576,8 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,
pinfo->dynamic_fps, pinfo->min_fps, pinfo->max_fps,
pinfo->panel_name, pinfo->is_prim_panel,
pinfo->is_pluggable, pinfo->display_id,
- pinfo->is_cec_supported, is_pingpong_split(mfd));
+ pinfo->is_cec_supported, is_pingpong_split(mfd),
+ dfps_porch_mode);
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 5a355f226179..518b84fbad51 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1950,6 +1950,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_set_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS);
mdata->has_wb_ubwc = true;
set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map);
+ set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map);
break;
default:
mdata->max_target_zorder = 4; /* excluding base layer */
@@ -2489,6 +2490,8 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev,
SPRINT(" separate_rotator");
if (test_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map))
SPRINT(" concurrent_writeback");
+ if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map))
+ SPRINT(" avr");
SPRINT("\n");
#undef SPRINT
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index da5e7bb8a343..da60570c7085 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -303,6 +303,11 @@ enum mdp_wb_blk_caps {
MDSS_MDP_WB_UBWC = BIT(3),
};
+enum mdss_mdp_avr_mode {
+ MDSS_MDP_AVR_CONTINUOUS = 0,
+ MDSS_MDP_AVR_ONE_SHOT,
+};
+
/**
* enum perf_calc_vote_mode - enum to decide if mdss_mdp_get_bw_vote_mode
* function needs an extra efficiency factor.
@@ -391,6 +396,7 @@ struct mdss_mdp_ctl_intfs_ops {
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
+ int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *);
};
struct mdss_mdp_cwb {
@@ -406,6 +412,11 @@ struct mdss_mdp_cwb {
struct work_struct cwb_work;
};
+struct mdss_mdp_avr_info {
+ bool avr_enabled;
+ int avr_mode;
+};
+
struct mdss_mdp_ctl {
u32 num;
char __iomem *base;
@@ -513,6 +524,7 @@ struct mdss_mdp_ctl {
/* dynamic resolution switch during cont-splash handoff */
bool switch_with_handoff;
+ struct mdss_mdp_avr_info avr_info;
};
struct mdss_mdp_mixer {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 3fc6d94393d5..1a0ba8f0e2a7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -5528,6 +5528,26 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
sctl = mdss_mdp_get_split_ctl(ctl);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
+ if (ctl->ops.avr_ctrl_fnc) {
+ ret = ctl->ops.avr_ctrl_fnc(ctl);
+ if (ret) {
+ pr_err("error configuring avr ctrl registers ctl=%d err=%d\n",
+ ctl->num, ret);
+ mutex_unlock(&ctl->lock);
+ return ret;
+ }
+ }
+
+ if (sctl && sctl->ops.avr_ctrl_fnc) {
+ ret = sctl->ops.avr_ctrl_fnc(sctl);
+ if (ret) {
+ pr_err("error configuring avr ctrl registers sctl=%d err=%d\n",
+ sctl->num, ret);
+ mutex_unlock(&ctl->lock);
+ return ret;
+ }
+ }
+
mutex_lock(&ctl->flush_lock);
/*
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 74ab902f6e8e..de868bcd8f6f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -688,6 +688,11 @@ enum mdss_mpd_intf_index {
#define MDSS_MDP_REG_INTF_PROG_LINE_INTR_CONF 0x250
#define MDSS_MDP_REG_INTF_VBLANK_END_CONF 0x264
+#define MDSS_MDP_REG_INTF_AVR_CONTROL 0x270
+#define MDSS_MDP_REG_INTF_AVR_MODE 0x274
+#define MDSS_MDP_REG_INTF_AVR_TRIGGER 0x278
+#define MDSS_MDP_REG_INTF_AVR_VTOTAL 0x27C
+
#define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8
#define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC
#define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index d1ced303b059..ee14fd0d0660 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -403,6 +403,76 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
}
}
+static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl,
+ struct intf_timing_params *p,
+ struct mdss_mdp_video_ctx *ctx)
+{
+ struct mdss_data_type *mdata = ctl->mdata;
+
+ if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) {
+ struct mdss_panel_data *pdata = ctl->panel_data;
+ u32 hsync_period = p->hsync_pulse_width + p->h_back_porch +
+ p->width + p->h_front_porch;
+ u32 vsync_period = p->vsync_pulse_width + p->v_back_porch +
+ p->height + p->v_front_porch;
+ u32 min_fps = pdata->panel_info.min_fps;
+ u32 diff_fps = abs(pdata->panel_info.default_fps - min_fps);
+ u32 vtotal = mdss_panel_get_vtotal(&pdata->panel_info);
+
+ int add_porches = mult_frac(vtotal, diff_fps, min_fps);
+
+ u32 vsync_period_slow = vsync_period + add_porches;
+ u32 avr_vtotal = vsync_period_slow * hsync_period;
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_VTOTAL, avr_vtotal);
+
+ MDSS_XLOG(min_fps, vsync_period, vsync_period_slow, avr_vtotal);
+ }
+}
+
+static int mdss_mdp_video_avr_trigger_setup(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = NULL;
+ struct mdss_data_type *mdata = ctl->mdata;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctx->ref_cnt) {
+ pr_err("invalid master ctx\n");
+ return -EINVAL;
+ }
+
+ if (!ctl->is_master)
+ return 0;
+
+ if (ctl->avr_info.avr_enabled &&
+ test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map))
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_TRIGGER, 1);
+
+ return 0;
+}
+
+static void mdss_mdp_video_avr_ctrl_setup(struct mdss_mdp_video_ctx *ctx,
+ struct mdss_mdp_avr_info *avr_info, bool is_master)
+{
+ u32 avr_ctrl = 0;
+ u32 avr_mode = 0;
+
+ avr_ctrl = avr_info->avr_enabled;
+ avr_mode = avr_info->avr_mode;
+
+ /* Enable avr_vsync_clear_en bit to clear avr in next vsync */
+ if (avr_mode == MDSS_MDP_AVR_ONE_SHOT)
+ avr_mode |= (1 << 8);
+
+ if (is_master)
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_CONTROL, avr_ctrl);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_MODE, avr_mode);
+
+ pr_debug("intf:%d avr_mode:%x avr_ctrl:%x\n",
+ ctx->intf_num, avr_mode, avr_ctrl);
+}
+
static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
struct intf_timing_params *p,
struct mdss_mdp_video_ctx *ctx)
@@ -1530,6 +1600,12 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
CTL_INTF_EVENT_FLAG_DEFAULT);
}
+ rc = mdss_mdp_video_avr_trigger_setup(ctl);
+ if (rc) {
+ pr_err("avr trigger setup failed\n");
+ return rc;
+ }
+
if (mdss_mdp_is_lineptr_supported(ctl))
mdss_mdp_video_lineptr_ctrl(ctl, true);
@@ -1886,6 +1962,8 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
mdss_mdp_handoff_programmable_fetch(ctl, ctx);
}
+ mdss_mdp_video_avr_vtotal_setup(ctl, itp, ctx);
+
mdss_mdp_disable_prefill(ctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format);
@@ -2124,6 +2202,29 @@ static int mdss_mdp_video_early_wake_up(struct mdss_mdp_ctl *ctl)
return 0;
}
+static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx = NULL, *sctx = NULL;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctx->ref_cnt) {
+ pr_err("invalid master ctx\n");
+ return -EINVAL;
+ }
+ mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master);
+
+ if (is_pingpong_split(ctl->mfd)) {
+ sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX];
+ if (!sctx || !sctx->ref_cnt) {
+ pr_err("invalid slave ctx\n");
+ return -EINVAL;
+ }
+ mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false);
+ }
+
+ return 0;
+}
+
int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
{
int intfs_num, ret = 0;
@@ -2144,6 +2245,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
ctl->ops.config_fps_fnc = mdss_mdp_video_config_fps;
ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up;
ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl;
+ ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl;
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 0ae420724d61..35bd0932f321 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -288,6 +288,57 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
return ret;
}
+static int mdss_mdp_avr_validate(struct msm_fb_data_type *mfd,
+ struct mdp_layer_commit_v1 *commit)
+{
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ int req = 0;
+ struct mdss_panel_info *pinfo = NULL;
+
+ if (!ctl || !mdata || !commit) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!(commit->flags & MDP_COMMIT_AVR_EN))
+ return 0;
+
+ pinfo = &ctl->panel_data->panel_info;
+
+ if (!test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map) ||
+ (pinfo->max_fps == pinfo->min_fps)) {
+ pr_err("AVR not supported\n");
+ return -ENODEV;
+ }
+
+ if (pinfo->dynamic_fps &&
+ !(pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP ||
+ pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP)) {
+ pr_err("Dynamic fps and AVR cannot coexists\n");
+ return -EINVAL;
+ }
+
+ if (!ctl->is_video_mode) {
+ pr_err("AVR not supported in command mode\n");
+ return -EINVAL;
+ }
+
+ return req;
+}
+
+static void __update_avr_info(struct mdss_mdp_ctl *ctl,
+ struct mdp_layer_commit_v1 *commit)
+{
+ if (commit->flags & MDP_COMMIT_AVR_EN)
+ ctl->avr_info.avr_enabled = true;
+
+ ctl->avr_info.avr_mode = MDSS_MDP_AVR_CONTINUOUS;
+
+ if (commit->flags & MDP_COMMIT_AVR_ONE_SHOT_MODE)
+ ctl->avr_info.avr_mode = MDSS_MDP_AVR_ONE_SHOT;
+}
+
/*
* __layer_needs_src_split() - check needs source split configuration
* @layer: input layer
@@ -2247,13 +2298,13 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_data *src_data[MDSS_MDP_MAX_SSPP];
struct mdss_mdp_validate_info_t *validate_info_list;
+ struct mdss_mdp_ctl *sctl = NULL;
mdp5_data = mfd_to_mdp5_data(mfd);
if (!mdp5_data || !mdp5_data->ctl)
return -EINVAL;
-
if (commit->output_layer) {
ret = __is_cwb_requested(commit->output_layer->flags);
if (IS_ERR_VALUE(ret)) {
@@ -2267,6 +2318,18 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
}
}
+ ret = mdss_mdp_avr_validate(mfd, commit);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("AVR validate failed\n");
+ return -EINVAL;
+ }
+
+ __update_avr_info(mdp5_data->ctl, commit);
+
+ sctl = mdss_mdp_get_split_ctl(mdp5_data->ctl);
+ if (sctl)
+ __update_avr_info(sctl, commit);
+
layer_list = commit->input_layers;
/* handle null commit */
@@ -2428,6 +2491,12 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
}
}
+ rc = mdss_mdp_avr_validate(mfd, commit);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("AVR validate failed\n");
+ return -EINVAL;
+ }
+
return __validate_layers(mfd, file, commit);
}
diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c
index 97025b3a9c23..61911810b2c0 100644
--- a/drivers/video/fbdev/msm/mdss_panel.c
+++ b/drivers/video/fbdev/msm/mdss_panel.c
@@ -644,6 +644,7 @@ void mdss_panel_info_from_timing(struct mdss_panel_timing *pt,
pinfo->dsc_enc_total = pt->dsc_enc_total;
pinfo->fbc = pt->fbc;
pinfo->compression_mode = pt->compression_mode;
+ pinfo->default_fps = pinfo->mipi.frame_rate;
pinfo->roi_alignment = pt->roi_alignment;
pinfo->te = pt->te;
diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h
index 8472224f33a6..811d8b4e1994 100644
--- a/include/uapi/linux/msm_mdp_ext.h
+++ b/include/uapi/linux/msm_mdp_ext.h
@@ -138,6 +138,15 @@ VALIDATE/COMMIT FLAG CONFIGURATION
*/
#define MDP_COMMIT_SYNC_FENCE_WAIT 0x04
+/* Flag to enable AVR(Adaptive variable refresh) feature. */
+#define MDP_COMMIT_AVR_EN 0x08
+
+/*
+ * Flag to select one shot mode when AVR feature is enabled.
+ * Default mode is continuous mode.
+ */
+#define MDP_COMMIT_AVR_ONE_SHOT_MODE 0x10
+
/* Flag to enable concurrent writeback for the frame */
#define MDP_COMMIT_CWB_EN 0x800
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index f5a71b2a2d1a..0af7314f1b7b 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -588,6 +588,8 @@ static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv);
static int tasha_config_compander(struct snd_soc_codec *, int, int);
static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool);
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable);
/* Hold instance to soundwire platform device */
struct tasha_swr_ctrl_data {
@@ -841,6 +843,8 @@ struct tasha_priv {
int hph_r_gain;
int rx_7_count;
int rx_8_count;
+ bool clk_mode;
+ bool clk_internal;
};
static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
@@ -2290,6 +2294,30 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tasha_get_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = tasha->clk_mode;
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
+static int tasha_put_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha->clk_mode = ucontrol->value.enumerated.item[0];
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
static int tasha_get_iir_enable_audio_mixer(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -3976,13 +4004,16 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
set_bit(HPH_PA_DELAY, &tasha->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
- if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0)
- /*
- * If PA_EN is not set (potentially in ANC case) then
- * do nothing for POST_PMU and let left channel handle
- * everything.
- */
- break;
+ if (!(strcmp(w->name, "ANC HPHR PA"))) {
+ if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let left
+ * channel handle everything.
+ */
+ break;
+ }
/*
* 7ms sleep is required after PA is enabled as per
* HW requirement
@@ -4067,13 +4098,16 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
set_bit(HPH_PA_DELAY, &tasha->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
- if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) != 0xC0)
- /*
- * If PA_EN is not set (potentially in ANC case) then
- * do nothing for POST_PMU and let right channel handle
- * everything.
- */
- break;
+ if (!(strcmp(w->name, "ANC HPHL PA"))) {
+ if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+ != 0xC0)
+ /*
+ * If PA_EN is not set (potentially in ANC case)
+ * then do nothing for POST_PMU and let right
+ * channel handle everything.
+ */
+ break;
+ }
/*
* 7ms sleep is required after PA is enabled as per
* HW requirement
@@ -6083,6 +6117,9 @@ static const char *const tasha_anc_func_text[] = {"OFF", "ON"};
static const struct soc_enum tasha_anc_func_enum =
SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text);
+static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"};
+static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text);
+
/* Cutoff frequency for high pass filter */
static const char * const cf_text[] = {
"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
@@ -8211,6 +8248,9 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = {
SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func,
tasha_put_anc_func),
+ SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode,
+ tasha_put_clkmode),
+
SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
@@ -11749,6 +11789,44 @@ int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm)
}
EXPORT_SYMBOL(tasha_cdc_mclk_enable);
+int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n",
+ __func__, tasha->clk_mode, enable, tasha->clk_internal);
+ if (tasha->clk_mode || tasha->clk_internal) {
+ if (enable) {
+ tasha_cdc_sido_ccl_enable(tasha, true);
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ set_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ SIDO_VOLTAGE_NOMINAL_MV);
+ tasha->clk_internal = true;
+ } else {
+ tasha->clk_internal = false;
+ clear_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ sido_buck_svs_voltage);
+ tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ tasha_cdc_sido_ccl_enable(tasha, false);
+ }
+ } else {
+ ret = __tasha_cdc_mclk_enable(tasha, enable);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable);
+
static ssize_t tasha_codec_version_read(struct snd_info_entry *entry,
void *file_private_data, struct file *file,
char __user *buf, size_t count, loff_t pos)
diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h
index 67280c583b2a..8d38399ba92f 100644
--- a/sound/soc/codecs/wcd9335.h
+++ b/sound/soc/codecs/wcd9335.h
@@ -179,6 +179,8 @@ extern void *tasha_get_afe_config(struct snd_soc_codec *codec,
enum afe_config_type config_type);
extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable,
bool dapm);
+extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable,
+ bool dapm);
extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec);
extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
struct wcd_mbhc_config *mbhc_cfg);
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
index 1d4f723dd572..631ac1a52864 100644
--- a/sound/soc/msm/msm8996.c
+++ b/sound/soc/msm/msm8996.c
@@ -2999,6 +2999,20 @@ static struct snd_soc_dai_link msm8996_tasha_fe_dai_links[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
};
static struct snd_soc_dai_link msm8996_common_be_dai_links[] = {