diff options
| -rw-r--r-- | Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt | 3 | ||||
| -rw-r--r-- | drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c | 3 | ||||
| -rw-r--r-- | drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c | 212 | ||||
| -rw-r--r-- | drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h | 5 | ||||
| -rw-r--r-- | drivers/platform/msm/gsi/gsi.c | 29 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss.h | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_panel.c | 3 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.c | 11 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.c | 3 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 12 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 20 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_hwio.h | 5 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_video.c | 102 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_layer.c | 71 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_panel.c | 1 | ||||
| -rw-r--r-- | include/uapi/linux/msm_mdp_ext.h | 9 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd9335.c | 106 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd9335.h | 2 | ||||
| -rw-r--r-- | sound/soc/msm/msm8996.c | 14 |
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[] = { |
