/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "msm_sdw.h" #include "msm_sdw_registers.h" #include "../msm-cdc-pinctrl.h" #define MSM_SDW_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) #define MSM_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S24_3LE) #define MSM_SDW_STRING_LEN 80 #define INT_MCLK1_FREQ 9600000 #define SDW_NPL_FREQ 153600000 #define MSM_SDW_VERSION_1_0 0x0001 #define MSM_SDW_VERSION_ENTRY_SIZE 32 /* * 200 Milliseconds sufficient for DSP bring up in the modem * after Sub System Restart */ #define ADSP_STATE_READY_TIMEOUT_MS 200 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); static struct snd_soc_dai_driver msm_sdw_dai[]; static bool skip_irq = true; static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec, int event, int gain_reg); static int msm_sdw_config_compander(struct snd_soc_codec *, int, int); static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw, int mclk_enable, bool dapm); static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, int enable, bool dapm); enum { VI_SENSE_1, VI_SENSE_2, }; enum { AIF1_SDW_PB = 0, AIF1_SDW_VIFEED, NUM_CODEC_DAIS, }; static const struct msm_sdw_reg_mask_val msm_sdw_spkr_default[] = { {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80}, {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x50}, {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x50}, }; static const struct msm_sdw_reg_mask_val msm_sdw_spkr_mode1[] = { {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x00}, {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x00}, {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x00}, {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x00}, {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x44}, {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x44}, }; /** * msm_sdw_set_spkr_gain_offset - offset the speaker path * gain with the given offset value. * * @codec: codec instance * @offset: Indicates speaker path gain offset value. * * Returns 0 on success or -EINVAL on error. */ int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) { struct msm_sdw_priv *priv; if (!codec) { pr_err("%s: NULL codec pointer!\n", __func__); return -EINVAL; } priv = snd_soc_codec_get_drvdata(codec); if (!priv) return -EINVAL; priv->spkr_gain_offset = offset; return 0; } EXPORT_SYMBOL(msm_sdw_set_spkr_gain_offset); /** * msm_sdw_set_spkr_mode - Configures speaker compander and smartboost * settings based on speaker mode. * * @codec: codec instance * @mode: Indicates speaker configuration mode. * * Returns 0 on success or -EINVAL on error. */ int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode) { struct msm_sdw_priv *priv; int i; const struct msm_sdw_reg_mask_val *regs; int size; if (!codec) { pr_err("%s: NULL codec pointer!\n", __func__); return -EINVAL; } priv = snd_soc_codec_get_drvdata(codec); if (!priv) return -EINVAL; switch (mode) { case SPKR_MODE_1: regs = msm_sdw_spkr_mode1; size = ARRAY_SIZE(msm_sdw_spkr_mode1); break; default: regs = msm_sdw_spkr_default; size = ARRAY_SIZE(msm_sdw_spkr_default); break; } priv->spkr_mode = mode; for (i = 0; i < size; i++) snd_soc_update_bits(codec, regs[i].reg, regs[i].mask, regs[i].val); return 0; } EXPORT_SYMBOL(msm_sdw_set_spkr_mode); static int msm_enable_sdw_npl_clk(struct msm_sdw_priv *msm_sdw, int enable) { int ret = 0; dev_dbg(msm_sdw->dev, "%s: enable %d\n", __func__, enable); mutex_lock(&msm_sdw->sdw_npl_clk_mutex); if (enable) { if (msm_sdw->sdw_npl_clk_enabled == false) { msm_sdw->sdw_npl_clk.enable = 1; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_npl_clk); if (ret < 0) { dev_err(msm_sdw->dev, "%s: failed to enable SDW NPL CLK\n", __func__); mutex_unlock(&msm_sdw->sdw_npl_clk_mutex); return ret; } dev_dbg(msm_sdw->dev, "enabled sdw npl clk\n"); msm_sdw->sdw_npl_clk_enabled = true; } } else { if (msm_sdw->sdw_npl_clk_enabled == true) { msm_sdw->sdw_npl_clk.enable = 0; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_npl_clk); if (ret < 0) dev_err(msm_sdw->dev, "%s: failed to disable SDW NPL CLK\n", __func__); msm_sdw->sdw_npl_clk_enabled = false; } } mutex_unlock(&msm_sdw->sdw_npl_clk_mutex); return ret; } static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, int enable, bool dapm) { int ret = 0; mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); dev_dbg(msm_sdw->dev, "%s: enable %d mclk1 ref counter %d\n", __func__, enable, msm_sdw->int_mclk1_rsc_ref); if (enable) { if (msm_sdw->int_mclk1_rsc_ref == 0) { cancel_delayed_work_sync( &msm_sdw->disable_int_mclk1_work); if (msm_sdw->int_mclk1_enabled == false) { msm_sdw->sdw_cdc_core_clk.enable = 1; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_cdc_core_clk); if (ret < 0) { dev_err(msm_sdw->dev, "%s: failed to enable SDW MCLK\n", __func__); goto rtn; } dev_dbg(msm_sdw->dev, "enabled sdw codec core mclk\n"); msm_sdw->int_mclk1_enabled = true; } } msm_sdw->int_mclk1_rsc_ref++; } else { cancel_delayed_work_sync(&msm_sdw->disable_int_mclk1_work); if (msm_sdw->int_mclk1_rsc_ref > 0) { msm_sdw->int_mclk1_rsc_ref--; dev_dbg(msm_sdw->dev, "%s: decrementing mclk_res_ref %d\n", __func__, msm_sdw->int_mclk1_rsc_ref); } if (msm_sdw->int_mclk1_enabled == true && msm_sdw->int_mclk1_rsc_ref == 0) { msm_sdw->sdw_cdc_core_clk.enable = 0; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_cdc_core_clk); if (ret < 0) dev_err(msm_sdw->dev, "%s: failed to disable SDW MCLK\n", __func__); msm_sdw->int_mclk1_enabled = false; } } mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); rtn: return ret; } EXPORT_SYMBOL(msm_int_enable_sdw_cdc_clk); static void msm_disable_int_mclk1(struct work_struct *work) { struct msm_sdw_priv *msm_sdw = NULL; struct delayed_work *dwork; int ret = 0; dwork = to_delayed_work(work); msm_sdw = container_of(dwork, struct msm_sdw_priv, disable_int_mclk1_work); mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); dev_dbg(msm_sdw->dev, "%s: mclk1_enabled %d mclk1_rsc_ref %d\n", __func__, msm_sdw->int_mclk1_enabled, msm_sdw->int_mclk1_rsc_ref); if (msm_sdw->int_mclk1_enabled == true && msm_sdw->int_mclk1_rsc_ref == 0) { dev_dbg(msm_sdw->dev, "Disable the mclk1\n"); msm_sdw->sdw_cdc_core_clk.enable = 0; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_cdc_core_clk); if (ret < 0) dev_err(msm_sdw->dev, "%s failed to disable the MCLK1\n", __func__); msm_sdw->int_mclk1_enabled = false; } mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); } static int msm_int_mclk1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); int ret = 0; dev_dbg(msm_sdw->dev, "%s: event = %d\n", __func__, event); switch (event) { case SND_SOC_DAPM_PRE_PMU: /* enable the codec mclk config */ msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true); msm_sdw_mclk_enable(msm_sdw, 1, true); break; case SND_SOC_DAPM_POST_PMD: /* disable the codec mclk config */ msm_sdw_mclk_enable(msm_sdw, 0, true); msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true); break; default: dev_err(msm_sdw->dev, "%s: invalid DAPM event %d\n", __func__, event); ret = -EINVAL; } return ret; } static int msm_sdw_ahb_write_device(struct msm_sdw_priv *msm_sdw, u16 reg, u8 *value) { u32 temp = (u32)(*value) & 0x000000FF; if (!msm_sdw->dev_up) { dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n", __func__); return 0; } iowrite32(temp, msm_sdw->sdw_base + reg); return 0; } static int msm_sdw_ahb_read_device(struct msm_sdw_priv *msm_sdw, u16 reg, u8 *value) { u32 temp; if (!msm_sdw->dev_up) { dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n", __func__); return 0; } temp = ioread32(msm_sdw->sdw_base + reg); *value = (u8)temp; return 0; } static int __msm_sdw_reg_read(struct msm_sdw_priv *msm_sdw, unsigned short reg, int bytes, void *dest) { int ret = -EINVAL, i; u8 temp = 0; dev_dbg(msm_sdw->dev, "%s reg = %x\n", __func__, reg); mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); if (msm_sdw->int_mclk1_enabled == false) { msm_sdw->sdw_cdc_core_clk.enable = 1; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_cdc_core_clk); if (ret < 0) { dev_err(msm_sdw->dev, "%s:failed to enable the INT_MCLK1\n", __func__); goto unlock_exit; } dev_dbg(msm_sdw->dev, "%s:enabled sdw codec core clk\n", __func__); for (i = 0; i < bytes; i++) { ret = msm_sdw_ahb_read_device( msm_sdw, reg + (4 * i), &temp); ((u8 *)dest)[i] = temp; } msm_sdw->int_mclk1_enabled = true; schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50); goto unlock_exit; } for (i = 0; i < bytes; i++) { ret = msm_sdw_ahb_read_device( msm_sdw, reg + (4 * i), &temp); ((u8 *)dest)[i] = temp; } unlock_exit: mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); if (ret < 0) { dev_err_ratelimited(msm_sdw->dev, "%s: codec read failed for reg 0x%x\n", __func__, reg); return ret; } dev_dbg(msm_sdw->dev, "Read 0x%02x from 0x%x\n", temp, reg); return 0; } static int __msm_sdw_reg_write(struct msm_sdw_priv *msm_sdw, unsigned short reg, int bytes, void *src) { int ret = -EINVAL, i; mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); if (msm_sdw->int_mclk1_enabled == false) { msm_sdw->sdw_cdc_core_clk.enable = 1; ret = afe_set_lpass_clock_v2(AFE_PORT_ID_INT4_MI2S_RX, &msm_sdw->sdw_cdc_core_clk); if (ret < 0) { dev_err(msm_sdw->dev, "%s: failed to enable the INT_MCLK1\n", __func__); ret = 0; goto unlock_exit; } dev_dbg(msm_sdw->dev, "%s: enabled INT_MCLK1\n", __func__); for (i = 0; i < bytes; i++) ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i), &((u8 *)src)[i]); msm_sdw->int_mclk1_enabled = true; schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50); goto unlock_exit; } for (i = 0; i < bytes; i++) ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i), &((u8 *)src)[i]); unlock_exit: mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); dev_dbg(msm_sdw->dev, "Write 0x%x val 0x%02x\n", reg, (u32)(*(u32 *)src)); return ret; } static int msm_sdw_codec_enable_vi_feedback(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = NULL; struct msm_sdw_priv *msm_sdw_p = NULL; int ret = 0; if (!w) { pr_err("%s invalid params\n", __func__); return -EINVAL; } codec = snd_soc_dapm_to_codec(w->dapm); msm_sdw_p = snd_soc_codec_get_drvdata(codec); dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n", __func__, codec->component.num_dai, w->sname); dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n", __func__, w->name, event, w->shift); if (w->shift != AIF1_SDW_VIFEED) { dev_err(codec->dev, "%s:Error in enabling the vi feedback path\n", __func__); ret = -EINVAL; goto out_vi; } switch (event) { case SND_SOC_DAPM_POST_PMU: if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) { dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); /* Enable V&I sensing */ snd_soc_update_bits(codec, MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x04); snd_soc_update_bits(codec, MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x0F, 0x04); snd_soc_update_bits(codec, MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); snd_soc_update_bits(codec, MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10, 0x10); snd_soc_update_bits(codec, MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); snd_soc_update_bits(codec, MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, 0x00); } if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) { dev_dbg(codec->dev, "%s: spkr2 enabled\n", __func__); /* Enable V&I sensing */ snd_soc_update_bits(codec, MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x0F, 0x04); snd_soc_update_bits(codec, MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x0F, 0x04); snd_soc_update_bits(codec, MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10, 0x10); snd_soc_update_bits(codec, MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10, 0x10); snd_soc_update_bits(codec, MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, 0x00); snd_soc_update_bits(codec, MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, 0x00); } break; case SND_SOC_DAPM_POST_PMD: if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) { /* Disable V&I sensing */ dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); snd_soc_update_bits(codec, MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); snd_soc_update_bits(codec, MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10, 0x00); } if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) { /* Disable V&I sensing */ dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); snd_soc_update_bits(codec, MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, 0x20); snd_soc_update_bits(codec, MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10, 0x00); snd_soc_update_bits(codec, MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10, 0x00); } break; } out_vi: return ret; } static int msm_sdwm_handle_irq(void *handle, irqreturn_t (*swrm_irq_handler)(int irq, void *data), void *swrm_handle, int action) { struct msm_sdw_priv *msm_sdw; int ret = 0; if (!handle) { pr_err("%s: null handle received\n", __func__); return -EINVAL; } msm_sdw = (struct msm_sdw_priv *) handle; if (skip_irq) return ret; if (action) { ret = request_threaded_irq(msm_sdw->sdw_irq, NULL, swrm_irq_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "swr_master_irq", swrm_handle); if (ret) dev_err(msm_sdw->dev, "%s: Failed to request irq %d\n", __func__, ret); } else free_irq(msm_sdw->sdw_irq, swrm_handle); return ret; } static void msm_sdw_codec_hd2_control(struct snd_soc_codec *codec, u16 reg, int event) { u16 hd2_scale_reg; u16 hd2_enable_reg = 0; if (reg == MSM_SDW_RX7_RX_PATH_CTL) { hd2_scale_reg = MSM_SDW_RX7_RX_PATH_SEC3; hd2_enable_reg = MSM_SDW_RX7_RX_PATH_CFG0; } if (reg == MSM_SDW_RX8_RX_PATH_CTL) { hd2_scale_reg = MSM_SDW_RX8_RX_PATH_SEC3; hd2_enable_reg = MSM_SDW_RX8_RX_PATH_CFG0; } if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); } if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); } } static int msm_sdw_enable_swr(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct msm_sdw_priv *msm_sdw; int i, ch_cnt; msm_sdw = snd_soc_codec_get_drvdata(codec); if (!msm_sdw->nr) return 0; switch (event) { case SND_SOC_DAPM_PRE_PMU: if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) && !msm_sdw->rx_4_count) msm_sdw->rx_4_count++; if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) && !msm_sdw->rx_5_count) msm_sdw->rx_5_count++; ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count; for (i = 0; i < msm_sdw->nr; i++) { swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, SWR_DEVICE_UP, NULL); swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, SWR_SET_NUM_RX_CH, &ch_cnt); } break; case SND_SOC_DAPM_POST_PMD: if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) && msm_sdw->rx_4_count) msm_sdw->rx_4_count--; if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) && msm_sdw->rx_5_count) msm_sdw->rx_5_count--; ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count; for (i = 0; i < msm_sdw->nr; i++) swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, SWR_SET_NUM_RX_CH, &ch_cnt); break; } dev_dbg(msm_sdw->dev, "%s: current swr ch cnt: %d\n", __func__, msm_sdw->rx_4_count + msm_sdw->rx_5_count); return 0; } static int msm_sdw_codec_enable_interpolator(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); u16 gain_reg; u16 reg; int val; int offset_val = 0; dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); if (!(strcmp(w->name, "RX INT4 INTERP"))) { reg = MSM_SDW_RX7_RX_PATH_CTL; gain_reg = MSM_SDW_RX7_RX_VOL_CTL; } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { reg = MSM_SDW_RX8_RX_PATH_CTL; gain_reg = MSM_SDW_RX8_RX_VOL_CTL; } else { dev_err(codec->dev, "%s: Interpolator reg not found\n", __func__); return -EINVAL; } switch (event) { case SND_SOC_DAPM_PRE_PMU: snd_soc_update_bits(codec, reg, 0x10, 0x10); msm_sdw_codec_hd2_control(codec, reg, event); snd_soc_update_bits(codec, reg, 1 << 0x5, 1 << 0x5); break; case SND_SOC_DAPM_POST_PMU: msm_sdw_config_compander(codec, w->shift, event); /* apply gain after int clk is enabled */ if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && (msm_sdw->comp_enabled[COMP1] || msm_sdw->comp_enabled[COMP2]) && (gain_reg == MSM_SDW_RX7_RX_VOL_CTL || gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) { snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1, 0x01, 0x01); snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_MIX_SEC0, 0x01, 0x01); snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1, 0x01, 0x01); snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_MIX_SEC0, 0x01, 0x01); offset_val = -2; } val = snd_soc_read(codec, gain_reg); val += offset_val; snd_soc_write(codec, gain_reg, val); msm_sdw_config_ear_spkr_gain(codec, event, gain_reg); snd_soc_update_bits(codec, reg, 0x10, 0x00); break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, reg, 1 << 0x5, 0 << 0x5); snd_soc_update_bits(codec, reg, 0x40, 0x40); snd_soc_update_bits(codec, reg, 0x40, 0x00); msm_sdw_codec_hd2_control(codec, reg, event); msm_sdw_config_compander(codec, w->shift, event); if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && (msm_sdw->comp_enabled[COMP1] || msm_sdw->comp_enabled[COMP2]) && (gain_reg == MSM_SDW_RX7_RX_VOL_CTL || gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) { snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1, 0x01, 0x00); snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_MIX_SEC0, 0x01, 0x00); snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1, 0x01, 0x00); snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_MIX_SEC0, 0x01, 0x00); offset_val = 2; val = snd_soc_read(codec, gain_reg); val += offset_val; snd_soc_write(codec, gain_reg, val); } msm_sdw_config_ear_spkr_gain(codec, event, gain_reg); break; }; return 0; } static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec, int event, int gain_reg) { int comp_gain_offset, val; struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); switch (msm_sdw->spkr_mode) { /* Compander gain in SPKR_MODE1 case is 12 dB */ case SPKR_MODE_1: comp_gain_offset = -12; break; /* Default case compander gain is 15 dB */ default: comp_gain_offset = -15; break; } switch (event) { case SND_SOC_DAPM_POST_PMU: /* Apply ear spkr gain only if compander is enabled */ if (msm_sdw->comp_enabled[COMP1] && (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) && (msm_sdw->ear_spkr_gain != 0)) { /* For example, val is -8(-12+5-1) for 4dB of gain */ val = comp_gain_offset + msm_sdw->ear_spkr_gain - 1; snd_soc_write(codec, gain_reg, val); dev_dbg(codec->dev, "%s: RX4 Volume %d dB\n", __func__, val); } break; case SND_SOC_DAPM_POST_PMD: /* * Reset RX4 volume to 0 dB if compander is enabled and * ear_spkr_gain is non-zero. */ if (msm_sdw->comp_enabled[COMP1] && (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) && (msm_sdw->ear_spkr_gain != 0)) { snd_soc_write(codec, gain_reg, 0x0); dev_dbg(codec->dev, "%s: Reset RX4 Volume to 0 dB\n", __func__); } break; } return 0; } static int msm_sdw_codec_spk_boost_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); u16 boost_path_ctl, boost_path_cfg1; u16 reg; dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); if (!strcmp(w->name, "RX INT4 CHAIN")) { boost_path_ctl = MSM_SDW_BOOST0_BOOST_PATH_CTL; boost_path_cfg1 = MSM_SDW_RX7_RX_PATH_CFG1; reg = MSM_SDW_RX7_RX_PATH_CTL; } else if (!strcmp(w->name, "RX INT5 CHAIN")) { boost_path_ctl = MSM_SDW_BOOST1_BOOST_PATH_CTL; boost_path_cfg1 = MSM_SDW_RX8_RX_PATH_CFG1; reg = MSM_SDW_RX8_RX_PATH_CTL; } else { dev_err(codec->dev, "%s: boost reg not found\n", __func__); return -EINVAL; } switch (event) { case SND_SOC_DAPM_PRE_PMU: snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); snd_soc_update_bits(codec, reg, 0x10, 0x00); break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); break; }; return 0; } static int msm_sdw_config_compander(struct snd_soc_codec *codec, int comp, int event) { struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); u16 comp_ctl0_reg, rx_path_cfg0_reg; if (comp < COMP1 || comp >= COMP_MAX) return 0; dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", __func__, event, comp + 1, msm_sdw->comp_enabled[comp]); if (!msm_sdw->comp_enabled[comp]) return 0; comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 0x20); rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 0x1E0); if (SND_SOC_DAPM_EVENT_ON(event)) { /* Enable Compander Clock */ snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); } if (SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); } return 0; } static int msm_sdw_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int comp = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = msm_sdw->comp_enabled[comp]; return 0; } static int msm_sdw_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); int comp = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; int value = ucontrol->value.integer.value[0]; dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", __func__, comp + 1, msm_sdw->comp_enabled[comp], value); msm_sdw->comp_enabled[comp] = value; return 0; } static int msm_sdw_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = msm_sdw->ear_spkr_gain; dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); return 0; } static int msm_sdw_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); msm_sdw->ear_spkr_gain = ucontrol->value.integer.value[0]; dev_dbg(codec->dev, "%s: gain = %d\n", __func__, msm_sdw->ear_spkr_gain); return 0; } static int msm_sdw_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u8 bst_state_max = 0; struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); bst_state_max = snd_soc_read(codec, MSM_SDW_BOOST0_BOOST_CTL); bst_state_max = (bst_state_max & 0x0c) >> 2; ucontrol->value.integer.value[0] = bst_state_max; dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); return 0; } static int msm_sdw_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u8 bst_state_max; struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); bst_state_max = ucontrol->value.integer.value[0] << 2; snd_soc_update_bits(codec, MSM_SDW_BOOST0_BOOST_CTL, 0x0c, bst_state_max); return 0; } static int msm_sdw_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u8 bst_state_max = 0; struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); bst_state_max = snd_soc_read(codec, MSM_SDW_BOOST1_BOOST_CTL); bst_state_max = (bst_state_max & 0x0c) >> 2; ucontrol->value.integer.value[0] = bst_state_max; dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); return 0; } static int msm_sdw_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u8 bst_state_max; struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); bst_state_max = ucontrol->value.integer.value[0] << 2; snd_soc_update_bits(codec, MSM_SDW_BOOST1_BOOST_CTL, 0x0c, bst_state_max); return 0; } static int msm_sdw_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = msm_sdw_p->vi_feed_value; return 0; } static int msm_sdw_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); struct soc_multi_mixer_control *mixer = ((struct soc_multi_mixer_control *)kcontrol->private_value); u32 dai_id = widget->shift; u32 port_id = mixer->shift; u32 enable = ucontrol->value.integer.value[0]; dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", __func__, enable, port_id, dai_id); msm_sdw_p->vi_feed_value = ucontrol->value.integer.value[0]; mutex_lock(&msm_sdw_p->codec_mutex); if (enable) { if (port_id == MSM_SDW_TX0 && !test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) set_bit(VI_SENSE_1, &msm_sdw_p->status_mask); if (port_id == MSM_SDW_TX1 && !test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) set_bit(VI_SENSE_2, &msm_sdw_p->status_mask); } else { if (port_id == MSM_SDW_TX0 && test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) clear_bit(VI_SENSE_1, &msm_sdw_p->status_mask); if (port_id == MSM_SDW_TX1 && test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) clear_bit(VI_SENSE_2, &msm_sdw_p->status_mask); } mutex_unlock(&msm_sdw_p->codec_mutex); snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); return 0; } static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw, int mclk_enable, bool dapm) { dev_dbg(msm_sdw->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", __func__, mclk_enable, dapm, msm_sdw->sdw_mclk_users); if (mclk_enable) { msm_sdw->sdw_mclk_users++; if (msm_sdw->sdw_mclk_users == 1) { regmap_update_bits(msm_sdw->regmap, MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01); regmap_update_bits(msm_sdw->regmap, MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, 0x01, 0x01); /* 9.6MHz MCLK, set value 0x00 if other frequency */ regmap_update_bits(msm_sdw->regmap, MSM_SDW_TOP_FREQ_MCLK, 0x01, 0x01); } } else { msm_sdw->sdw_mclk_users--; if (msm_sdw->sdw_mclk_users == 0) { regmap_update_bits(msm_sdw->regmap, MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x00); regmap_update_bits(msm_sdw->regmap, MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, 0x01, 0x00); } } return 0; } EXPORT_SYMBOL(msm_sdw_mclk_enable); static int msm_sdw_swrm_read(void *handle, int reg) { struct msm_sdw_priv *msm_sdw; unsigned short sdw_rd_addr_base; unsigned short sdw_rd_data_base; int val, ret; if (!handle) { pr_err("%s: NULL handle\n", __func__); return -EINVAL; } msm_sdw = (struct msm_sdw_priv *)handle; dev_dbg(msm_sdw->dev, "%s: Reading soundwire register, 0x%x\n", __func__, reg); sdw_rd_addr_base = MSM_SDW_AHB_BRIDGE_RD_ADDR_0; sdw_rd_data_base = MSM_SDW_AHB_BRIDGE_RD_DATA_0; /* * Add sleep as SWR slave access read takes time. * Allow for RD_DONE to complete for previous register if any. */ usleep_range(100, 105); /* read_lock */ mutex_lock(&msm_sdw->sdw_read_lock); ret = regmap_bulk_write(msm_sdw->regmap, sdw_rd_addr_base, (u8 *)®, 4); if (ret < 0) { dev_err(msm_sdw->dev, "%s: RD Addr Failure\n", __func__); goto err; } /* Add sleep for SWR register read value to get updated. */ usleep_range(100, 105); /* Check for RD value */ ret = regmap_bulk_read(msm_sdw->regmap, sdw_rd_data_base, (u8 *)&val, 4); if (ret < 0) { dev_err(msm_sdw->dev, "%s: RD Data Failure\n", __func__); goto err; } ret = val; err: /* read_unlock */ mutex_unlock(&msm_sdw->sdw_read_lock); return ret; } static int msm_sdw_bulk_write(struct msm_sdw_priv *msm_sdw, struct msm_sdw_reg_val *bulk_reg, size_t len) { int i, ret = 0; unsigned short sdw_wr_addr_base; unsigned short sdw_wr_data_base; sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; for (i = 0; i < len; i += 2) { /* * Add sleep as SWR slave write takes time. * Allow for any previous pending write to complete. */ usleep_range(100, 105); /* First Write the Data to register */ ret = regmap_bulk_write(msm_sdw->regmap, sdw_wr_data_base, bulk_reg[i].buf, 4); if (ret < 0) { dev_err(msm_sdw->dev, "%s: WR Data Failure\n", __func__); break; } /* Next Write Address */ ret = regmap_bulk_write(msm_sdw->regmap, sdw_wr_addr_base, bulk_reg[i+1].buf, 4); if (ret < 0) { dev_err(msm_sdw->dev, "%s: WR Addr Failure: 0x%x\n", __func__, (u32)(bulk_reg[i+1].buf[0])); break; } } return ret; } static int msm_sdw_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) { struct msm_sdw_priv *msm_sdw; struct msm_sdw_reg_val *bulk_reg; unsigned short sdw_wr_addr_base; unsigned short sdw_wr_data_base; int i, j, ret; if (!handle) { pr_err("%s: NULL handle\n", __func__); return -EINVAL; } msm_sdw = (struct msm_sdw_priv *)handle; if (len <= 0) { dev_err(msm_sdw->dev, "%s: Invalid size: %zu\n", __func__, len); return -EINVAL; } sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; bulk_reg = kzalloc((2 * len * sizeof(struct msm_sdw_reg_val)), GFP_KERNEL); if (!bulk_reg) return -ENOMEM; for (i = 0, j = 0; i < (len * 2); i += 2, j++) { bulk_reg[i].reg = sdw_wr_data_base; bulk_reg[i].buf = (u8 *)(&val[j]); bulk_reg[i].bytes = 4; bulk_reg[i+1].reg = sdw_wr_addr_base; bulk_reg[i+1].buf = (u8 *)(®[j]); bulk_reg[i+1].bytes = 4; } mutex_lock(&msm_sdw->sdw_write_lock); ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, (len * 2)); if (ret) dev_err(msm_sdw->dev, "%s: swrm bulk write failed, ret: %d\n", __func__, ret); mutex_unlock(&msm_sdw->sdw_write_lock); kfree(bulk_reg); return ret; } static int msm_sdw_swrm_write(void *handle, int reg, int val) { struct msm_sdw_priv *msm_sdw; unsigned short sdw_wr_addr_base; unsigned short sdw_wr_data_base; struct msm_sdw_reg_val bulk_reg[2]; int ret; if (!handle) { pr_err("%s: NULL handle\n", __func__); return -EINVAL; } msm_sdw = (struct msm_sdw_priv *)handle; sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; /* First Write the Data to register */ bulk_reg[0].reg = sdw_wr_data_base; bulk_reg[0].buf = (u8 *)(&val); bulk_reg[0].bytes = 4; bulk_reg[1].reg = sdw_wr_addr_base; bulk_reg[1].buf = (u8 *)(®); bulk_reg[1].bytes = 4; mutex_lock(&msm_sdw->sdw_write_lock); ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, 2); if (ret < 0) dev_err(msm_sdw->dev, "%s: WR Data Failure\n", __func__); mutex_unlock(&msm_sdw->sdw_write_lock); return ret; } static int msm_sdw_swrm_clock(void *handle, bool enable) { struct msm_sdw_priv *msm_sdw = (struct msm_sdw_priv *) handle; mutex_lock(&msm_sdw->sdw_clk_lock); dev_dbg(msm_sdw->dev, "%s: swrm clock %s\n", __func__, (enable ? "enable" : "disable")); if (enable) { msm_sdw->sdw_clk_users++; if (msm_sdw->sdw_clk_users == 1) { msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true); msm_sdw_mclk_enable(msm_sdw, 1, true); regmap_update_bits(msm_sdw->regmap, MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x01); msm_enable_sdw_npl_clk(msm_sdw, true); msm_cdc_pinctrl_select_active_state( msm_sdw->sdw_gpio_p); } } else { msm_sdw->sdw_clk_users--; if (msm_sdw->sdw_clk_users == 0) { regmap_update_bits(msm_sdw->regmap, MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x00); msm_sdw_mclk_enable(msm_sdw, 0, true); msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true); msm_enable_sdw_npl_clk(msm_sdw, false); msm_cdc_pinctrl_select_sleep_state(msm_sdw->sdw_gpio_p); } } dev_dbg(msm_sdw->dev, "%s: swrm clock users %d\n", __func__, msm_sdw->sdw_clk_users); mutex_unlock(&msm_sdw->sdw_clk_lock); return 0; } static int msm_sdw_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", __func__, substream->name, substream->stream); return 0; } static int msm_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { u8 clk_fs_rate, fs_rate; dev_dbg(dai->codec->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", __func__, dai->name, dai->id, params_rate(params), params_channels(params), params_format(params)); switch (params_rate(params)) { case 8000: clk_fs_rate = 0x00; fs_rate = 0x00; break; case 16000: clk_fs_rate = 0x01; fs_rate = 0x01; break; case 32000: clk_fs_rate = 0x02; fs_rate = 0x03; break; case 48000: clk_fs_rate = 0x03; fs_rate = 0x04; break; case 96000: clk_fs_rate = 0x04; fs_rate = 0x05; break; case 192000: clk_fs_rate = 0x05; fs_rate = 0x06; break; default: dev_err(dai->codec->dev, "%s: Invalid sampling rate %d\n", __func__, params_rate(params)); return -EINVAL; } if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { snd_soc_update_bits(dai->codec, MSM_SDW_TOP_TX_I2S_CTL, 0x1C, (clk_fs_rate << 2)); } else { snd_soc_update_bits(dai->codec, MSM_SDW_TOP_RX_I2S_CTL, 0x1C, (clk_fs_rate << 2)); snd_soc_update_bits(dai->codec, MSM_SDW_RX7_RX_PATH_CTL, 0x0F, fs_rate); snd_soc_update_bits(dai->codec, MSM_SDW_RX8_RX_PATH_CTL, 0x0F, fs_rate); } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) snd_soc_update_bits(dai->codec, MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x20); else snd_soc_update_bits(dai->codec, MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x20); break; case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_3LE: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) snd_soc_update_bits(dai->codec, MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x00); else snd_soc_update_bits(dai->codec, MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x00); break; default: dev_err(dai->codec->dev, "%s: wrong format selected\n", __func__); return -EINVAL; } return 0; } static void msm_sdw_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", __func__, substream->name, substream->stream); } static ssize_t msm_sdw_codec_version_read(struct snd_info_entry *entry, void *file_private_data, struct file *file, char __user *buf, size_t count, loff_t pos) { struct msm_sdw_priv *msm_sdw; char buffer[MSM_SDW_VERSION_ENTRY_SIZE]; int len = 0; msm_sdw = (struct msm_sdw_priv *) entry->private_data; if (!msm_sdw) { pr_err("%s: msm_sdw priv is null\n", __func__); return -EINVAL; } switch (msm_sdw->version) { case MSM_SDW_VERSION_1_0: len = snprintf(buffer, sizeof(buffer), "SDW-CDC_1_0\n"); break; default: len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); } return simple_read_from_buffer(buf, count, &pos, buffer, len); } static struct snd_info_entry_ops msm_sdw_codec_info_ops = { .read = msm_sdw_codec_version_read, }; /* * msm_sdw_codec_info_create_codec_entry - creates msm_sdw module * @codec_root: The parent directory * @codec: Codec instance * * Creates msm_sdw module and version entry under the given * parent directory. * * Return: 0 on success or negative error code on failure. */ int msm_sdw_codec_info_create_codec_entry(struct snd_info_entry *codec_root, struct snd_soc_codec *codec) { struct snd_info_entry *version_entry; struct msm_sdw_priv *msm_sdw; struct snd_soc_card *card; char name[80]; if (!codec_root || !codec) return -EINVAL; msm_sdw = snd_soc_codec_get_drvdata(codec); card = codec->component.card; snprintf(name, sizeof(name), "%x.%s", (u32)msm_sdw->sdw_base_addr, "msm-sdw-codec"); msm_sdw->entry = snd_info_create_subdir(codec_root->module, (const char *)name, codec_root); if (!msm_sdw->entry) { dev_err(codec->dev, "%s: failed to create msm_sdw entry\n", __func__); return -ENOMEM; } version_entry = snd_info_create_card_entry(card->snd_card, "version", msm_sdw->entry); if (!version_entry) { dev_err(codec->dev, "%s: failed to create msm_sdw version entry\n", __func__); return -ENOMEM; } version_entry->private_data = msm_sdw; version_entry->size = MSM_SDW_VERSION_ENTRY_SIZE; version_entry->content = SNDRV_INFO_CONTENT_DATA; version_entry->c.ops = &msm_sdw_codec_info_ops; if (snd_info_register(version_entry) < 0) { snd_info_free_entry(version_entry); return -ENOMEM; } msm_sdw->version_entry = version_entry; return 0; } EXPORT_SYMBOL(msm_sdw_codec_info_create_codec_entry); static struct snd_soc_dai_ops msm_sdw_dai_ops = { .startup = msm_sdw_startup, .shutdown = msm_sdw_shutdown, .hw_params = msm_sdw_hw_params, }; static struct snd_soc_dai_driver msm_sdw_dai[] = { { .name = "msm_sdw_i2s_rx1", .id = AIF1_SDW_PB, .playback = { .stream_name = "AIF1_SDW Playback", .rates = MSM_SDW_RATES, .formats = MSM_SDW_FORMATS, .rate_max = 192000, .rate_min = 8000, .channels_min = 1, .channels_max = 4, }, .ops = &msm_sdw_dai_ops, }, { .name = "msm_sdw_vifeedback", .id = AIF1_SDW_VIFEED, .capture = { .stream_name = "VIfeed_SDW", .rates = MSM_SDW_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, .rate_max = 48000, .rate_min = 8000, .channels_min = 2, .channels_max = 4, }, .ops = &msm_sdw_dai_ops, }, }; static const char * const rx_mix1_text[] = { "ZERO", "RX4", "RX5" }; static const char * const msm_sdw_ear_spkr_pa_gain_text[] = { "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB", "G_5_DB", "G_6_DB" }; static const char * const msm_sdw_speaker_boost_stage_text[] = { "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" }; static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_ear_spkr_pa_gain_enum, msm_sdw_ear_spkr_pa_gain_text); static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_spkr_boost_stage_enum, msm_sdw_speaker_boost_stage_text); /* RX4 MIX1 */ static const struct soc_enum rx4_mix1_inp1_chain_enum = SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, 0, 3, rx_mix1_text); static const struct soc_enum rx4_mix1_inp2_chain_enum = SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT1_MUX, 0, 3, rx_mix1_text); /* RX5 MIX1 */ static const struct soc_enum rx5_mix1_inp1_chain_enum = SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT0_MUX, 0, 3, rx_mix1_text); static const struct soc_enum rx5_mix1_inp2_chain_enum = SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT1_MUX, 0, 3, rx_mix1_text); static const struct snd_kcontrol_new rx4_mix1_inp1_mux = SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum); static const struct snd_kcontrol_new rx4_mix1_inp2_mux = SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum); static const struct snd_kcontrol_new rx5_mix1_inp1_mux = SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum); static const struct snd_kcontrol_new rx5_mix1_inp2_mux = SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum); static const struct snd_kcontrol_new aif1_vi_mixer[] = { SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, MSM_SDW_TX0, 1, 0, msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put), SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, MSM_SDW_TX1, 1, 0, msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put), }; static const struct snd_soc_dapm_widget msm_sdw_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("I2S RX4", "AIF1_SDW Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("I2S RX5", "AIF1_SDW Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT_E("AIF1_SDW VI", "VIfeed_SDW", 0, SND_SOC_NOPM, AIF1_SDW_VIFEED, 0, msm_sdw_codec_enable_vi_feedback, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("AIF1_VI_SDW Mixer", SND_SOC_NOPM, AIF1_SDW_VIFEED, 0, aif1_vi_mixer, ARRAY_SIZE(aif1_vi_mixer)), SND_SOC_DAPM_MUX_E("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx4_mix1_inp1_mux, msm_sdw_enable_swr, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX_E("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx4_mix1_inp2_mux, msm_sdw_enable_swr, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX_E("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx5_mix1_inp1_mux, msm_sdw_enable_swr, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX_E("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx5_mix1_inp2_mux, msm_sdw_enable_swr, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("RX4 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("RX5 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER_E("RX INT4 INTERP", SND_SOC_NOPM, COMP1, 0, NULL, 0, msm_sdw_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("RX INT5 INTERP", SND_SOC_NOPM, COMP2, 0, NULL, 0, msm_sdw_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("RX INT4 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0, msm_sdw_codec_spk_boost_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("RX INT5 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0, msm_sdw_codec_spk_boost_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_INPUT("VIINPUT_SDW"), SND_SOC_DAPM_OUTPUT("SPK1 OUT"), SND_SOC_DAPM_OUTPUT("SPK2 OUT"), SND_SOC_DAPM_SUPPLY_S("SDW_CONN", -1, MSM_SDW_TOP_I2S_CLK, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("INT_MCLK1", -2, SND_SOC_NOPM, 0, 0, msm_int_mclk1_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("SDW_RX_I2S_CLK", MSM_SDW_TOP_RX_I2S_CTL, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("SDW_TX_I2S_CLK", MSM_SDW_TOP_TX_I2S_CTL, 0, 0, NULL, 0), }; static const struct snd_kcontrol_new msm_sdw_snd_controls[] = { SOC_ENUM_EXT("EAR SPKR PA Gain", msm_sdw_ear_spkr_pa_gain_enum, msm_sdw_ear_spkr_pa_gain_get, msm_sdw_ear_spkr_pa_gain_put), SOC_ENUM_EXT("SPKR Left Boost Max State", msm_sdw_spkr_boost_stage_enum, msm_sdw_spkr_left_boost_stage_get, msm_sdw_spkr_left_boost_stage_put), SOC_ENUM_EXT("SPKR Right Boost Max State", msm_sdw_spkr_boost_stage_enum, msm_sdw_spkr_right_boost_stage_get, msm_sdw_spkr_right_boost_stage_put), SOC_SINGLE_SX_TLV("RX4 Digital Volume", MSM_SDW_RX7_RX_VOL_CTL, 0, -84, 40, digital_gain), SOC_SINGLE_SX_TLV("RX5 Digital Volume", MSM_SDW_RX8_RX_VOL_CTL, 0, -84, 40, digital_gain), SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMP1, 1, 0, msm_sdw_get_compander, msm_sdw_set_compander), SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMP2, 1, 0, msm_sdw_get_compander, msm_sdw_set_compander), }; static const struct snd_soc_dapm_route audio_map[] = { {"AIF1_SDW VI", NULL, "SDW_TX_I2S_CLK"}, {"SDW_TX_I2S_CLK", NULL, "INT_MCLK1"}, {"SDW_TX_I2S_CLK", NULL, "SDW_CONN"}, /* VI Feedback */ {"AIF1_VI_SDW Mixer", "SPKR_VI_1", "VIINPUT_SDW"}, {"AIF1_VI_SDW Mixer", "SPKR_VI_2", "VIINPUT_SDW"}, {"AIF1_SDW VI", NULL, "AIF1_VI_SDW Mixer"}, {"SDW_RX_I2S_CLK", NULL, "INT_MCLK1"}, {"SDW_RX_I2S_CLK", NULL, "SDW_CONN"}, {"I2S RX4", NULL, "SDW_RX_I2S_CLK"}, {"I2S RX5", NULL, "SDW_RX_I2S_CLK"}, {"RX4 MIX1 INP1", "RX4", "I2S RX4"}, {"RX4 MIX1 INP1", "RX5", "I2S RX5"}, {"RX4 MIX1 INP2", "RX4", "I2S RX4"}, {"RX4 MIX1 INP2", "RX5", "I2S RX5"}, {"RX5 MIX1 INP1", "RX4", "I2S RX4"}, {"RX5 MIX1 INP1", "RX5", "I2S RX5"}, {"RX5 MIX1 INP2", "RX4", "I2S RX4"}, {"RX5 MIX1 INP2", "RX5", "I2S RX5"}, {"RX4 MIX1", NULL, "RX4 MIX1 INP1"}, {"RX4 MIX1", NULL, "RX4 MIX1 INP2"}, {"RX5 MIX1", NULL, "RX5 MIX1 INP1"}, {"RX5 MIX1", NULL, "RX5 MIX1 INP2"}, {"RX INT4 INTERP", NULL, "RX4 MIX1"}, {"RX INT4 CHAIN", NULL, "RX INT4 INTERP"}, {"SPK1 OUT", NULL, "RX INT4 CHAIN"}, {"RX INT5 INTERP", NULL, "RX5 MIX1"}, {"RX INT5 CHAIN", NULL, "RX INT5 INTERP"}, {"SPK2 OUT", NULL, "RX INT5 CHAIN"}, }; static const struct msm_sdw_reg_mask_val msm_sdw_reg_init[] = { {MSM_SDW_BOOST0_BOOST_CFG1, 0x3F, 0x12}, {MSM_SDW_BOOST0_BOOST_CFG2, 0x1C, 0x08}, {MSM_SDW_COMPANDER7_CTL7, 0x1E, 0x18}, {MSM_SDW_BOOST1_BOOST_CFG1, 0x3F, 0x12}, {MSM_SDW_BOOST1_BOOST_CFG2, 0x1C, 0x08}, {MSM_SDW_COMPANDER8_CTL7, 0x1E, 0x18}, {MSM_SDW_BOOST0_BOOST_CTL, 0x70, 0x50}, {MSM_SDW_BOOST1_BOOST_CTL, 0x70, 0x50}, {MSM_SDW_RX7_RX_PATH_CFG1, 0x08, 0x08}, {MSM_SDW_RX8_RX_PATH_CFG1, 0x08, 0x08}, {MSM_SDW_TOP_TOP_CFG1, 0x02, 0x02}, {MSM_SDW_TOP_TOP_CFG1, 0x01, 0x01}, {MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, {MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, {MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, {MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80}, {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, {MSM_SDW_RX7_RX_PATH_CFG0, 0x01, 0x01}, {MSM_SDW_RX8_RX_PATH_CFG0, 0x01, 0x01}, {MSM_SDW_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, {MSM_SDW_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, }; static void msm_sdw_init_reg(struct snd_soc_codec *codec) { int i; for (i = 0; i < ARRAY_SIZE(msm_sdw_reg_init); i++) snd_soc_update_bits(codec, msm_sdw_reg_init[i].reg, msm_sdw_reg_init[i].mask, msm_sdw_reg_init[i].val); } static int msm_sdw_notifier_service_cb(struct notifier_block *nb, unsigned long opcode, void *ptr) { int i; struct msm_sdw_priv *msm_sdw = container_of(nb, struct msm_sdw_priv, service_nb); bool adsp_ready = false; unsigned long timeout; static bool initial_boot = true; pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); mutex_lock(&msm_sdw->codec_mutex); switch (opcode) { case AUDIO_NOTIFIER_SERVICE_DOWN: if (initial_boot) { initial_boot = false; break; } msm_sdw->int_mclk1_enabled = false; msm_sdw->dev_up = false; for (i = 0; i < msm_sdw->nr; i++) swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, SWR_DEVICE_DOWN, NULL); break; case AUDIO_NOTIFIER_SERVICE_UP: if (initial_boot) initial_boot = false; if (!q6core_is_adsp_ready()) { dev_dbg(msm_sdw->dev, "ADSP isn't ready\n"); timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); while (!time_after(jiffies, timeout)) { if (!q6core_is_adsp_ready()) { dev_dbg(msm_sdw->dev, "ADSP isn't ready\n"); } else { dev_dbg(msm_sdw->dev, "ADSP is ready\n"); adsp_ready = true; goto powerup; } } } else { adsp_ready = true; dev_dbg(msm_sdw->dev, "%s: DSP is ready\n", __func__); } powerup: if (adsp_ready) { msm_sdw->dev_up = true; msm_sdw_init_reg(msm_sdw->codec); regcache_mark_dirty(msm_sdw->regmap); regcache_sync(msm_sdw->regmap); msm_sdw_set_spkr_mode(msm_sdw->codec, msm_sdw->spkr_mode); } break; default: break; } mutex_unlock(&msm_sdw->codec_mutex); return NOTIFY_OK; } static int msm_sdw_codec_probe(struct snd_soc_codec *codec) { struct msm_sdw_priv *msm_sdw; int i, ret; msm_sdw = snd_soc_codec_get_drvdata(codec); if (!msm_sdw) { pr_err("%s:SDW priv data null\n", __func__); return -EINVAL; } msm_sdw->codec = codec; for (i = 0; i < COMP_MAX; i++) msm_sdw->comp_enabled[i] = 0; msm_sdw->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; msm_sdw_init_reg(codec); msm_sdw->version = MSM_SDW_VERSION_1_0; msm_sdw->service_nb.notifier_call = msm_sdw_notifier_service_cb; ret = audio_notifier_register("msm_sdw", AUDIO_NOTIFIER_ADSP_DOMAIN, &msm_sdw->service_nb); if (ret < 0) dev_err(msm_sdw->dev, "%s: Audio notifier register failed ret = %d\n", __func__, ret); return 0; } static int msm_sdw_codec_remove(struct snd_soc_codec *codec) { return 0; } static struct regmap *msm_sdw_get_regmap(struct device *dev) { struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); return msm_sdw->regmap; } static struct snd_soc_codec_driver soc_codec_dev_msm_sdw = { .probe = msm_sdw_codec_probe, .remove = msm_sdw_codec_remove, .get_regmap = msm_sdw_get_regmap, .component_driver = { .controls = msm_sdw_snd_controls, .num_controls = ARRAY_SIZE(msm_sdw_snd_controls), .dapm_widgets = msm_sdw_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(msm_sdw_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), }, }; static void msm_sdw_add_child_devices(struct work_struct *work) { struct msm_sdw_priv *msm_sdw; struct platform_device *pdev; struct device_node *node; struct msm_sdw_ctrl_data *sdw_ctrl_data = NULL, *temp; int ret, ctrl_num = 0; struct wcd_sdw_ctrl_platform_data *platdata; char plat_dev_name[MSM_SDW_STRING_LEN]; msm_sdw = container_of(work, struct msm_sdw_priv, msm_sdw_add_child_devices_work); if (!msm_sdw) { pr_err("%s: Memory for msm_sdw does not exist\n", __func__); return; } if (!msm_sdw->dev->of_node) { dev_err(msm_sdw->dev, "%s: DT node for msm_sdw does not exist\n", __func__); return; } platdata = &msm_sdw->sdw_plat_data; for_each_available_child_of_node(msm_sdw->dev->of_node, node) { if (!strcmp(node->name, "swr_master")) strlcpy(plat_dev_name, "msm_sdw_swr_ctrl", (MSM_SDW_STRING_LEN - 1)); else if (strnstr(node->name, "msm_cdc_pinctrl", strlen("msm_cdc_pinctrl")) != NULL) strlcpy(plat_dev_name, node->name, (MSM_SDW_STRING_LEN - 1)); else continue; pdev = platform_device_alloc(plat_dev_name, -1); if (!pdev) { dev_err(msm_sdw->dev, "%s: pdev memory alloc failed\n", __func__); ret = -ENOMEM; goto err; } pdev->dev.parent = msm_sdw->dev; pdev->dev.of_node = node; if (!strcmp(node->name, "swr_master")) { ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); if (ret) { dev_err(&pdev->dev, "%s: cannot add plat data ctrl:%d\n", __func__, ctrl_num); goto fail_pdev_add; } } ret = platform_device_add(pdev); if (ret) { dev_err(&pdev->dev, "%s: Cannot add platform device\n", __func__); goto fail_pdev_add; } if (!strcmp(node->name, "swr_master")) { temp = krealloc(sdw_ctrl_data, (ctrl_num + 1) * sizeof( struct msm_sdw_ctrl_data), GFP_KERNEL); if (!temp) { dev_err(&pdev->dev, "out of memory\n"); ret = -ENOMEM; goto err; } sdw_ctrl_data = temp; sdw_ctrl_data[ctrl_num].sdw_pdev = pdev; ctrl_num++; dev_dbg(&pdev->dev, "%s: Added soundwire ctrl device(s)\n", __func__); msm_sdw->nr = ctrl_num; msm_sdw->sdw_ctrl_data = sdw_ctrl_data; } } return; fail_pdev_add: platform_device_put(pdev); err: return; } static int msm_sdw_probe(struct platform_device *pdev) { int ret = 0; struct msm_sdw_priv *msm_sdw; int adsp_state; adsp_state = apr_get_subsys_state(); if (adsp_state != APR_SUBSYS_LOADED) { dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", adsp_state); return -EPROBE_DEFER; } msm_sdw = devm_kzalloc(&pdev->dev, sizeof(struct msm_sdw_priv), GFP_KERNEL); if (!msm_sdw) return -ENOMEM; dev_set_drvdata(&pdev->dev, msm_sdw); msm_sdw->dev_up = true; msm_sdw->dev = &pdev->dev; INIT_WORK(&msm_sdw->msm_sdw_add_child_devices_work, msm_sdw_add_child_devices); msm_sdw->sdw_plat_data.handle = (void *) msm_sdw; msm_sdw->sdw_plat_data.read = msm_sdw_swrm_read; msm_sdw->sdw_plat_data.write = msm_sdw_swrm_write; msm_sdw->sdw_plat_data.bulk_write = msm_sdw_swrm_bulk_write; msm_sdw->sdw_plat_data.clk = msm_sdw_swrm_clock; msm_sdw->sdw_plat_data.handle_irq = msm_sdwm_handle_irq; ret = of_property_read_u32(pdev->dev.of_node, "reg", &msm_sdw->sdw_base_addr); if (ret) { dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", __func__, "reg"); goto err_sdw_cdc; } msm_sdw->sdw_gpio_p = of_parse_phandle(pdev->dev.of_node, "qcom,cdc-sdw-gpios", 0); msm_sdw->sdw_base = ioremap(msm_sdw->sdw_base_addr, MSM_SDW_MAX_REGISTER); msm_sdw->read_dev = __msm_sdw_reg_read; msm_sdw->write_dev = __msm_sdw_reg_write; msm_sdw->regmap = msm_sdw_regmap_init(msm_sdw->dev, &msm_sdw_regmap_config); msm_sdw->sdw_irq = platform_get_irq_byname(pdev, "swr_master_irq"); if (msm_sdw->sdw_irq < 0) { dev_err(msm_sdw->dev, "%s() error getting irq handle: %d\n", __func__, msm_sdw->sdw_irq); ret = -ENODEV; goto err_sdw_cdc; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm_sdw, msm_sdw_dai, ARRAY_SIZE(msm_sdw_dai)); if (ret) { dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", __func__, ret); goto err_sdw_cdc; } /* initialize the int_mclk1 */ msm_sdw->sdw_cdc_core_clk.clk_set_minor_version = AFE_API_VERSION_I2S_CONFIG; msm_sdw->sdw_cdc_core_clk.clk_id = Q6AFE_LPASS_CLK_ID_INT_MCLK_1; msm_sdw->sdw_cdc_core_clk.clk_freq_in_hz = INT_MCLK1_FREQ; msm_sdw->sdw_cdc_core_clk.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; msm_sdw->sdw_cdc_core_clk.clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT; msm_sdw->sdw_cdc_core_clk.enable = 0; /* initialize the sdw_npl_clk */ msm_sdw->sdw_npl_clk.clk_set_minor_version = AFE_API_VERSION_I2S_CONFIG; msm_sdw->sdw_npl_clk.clk_id = AFE_CLOCK_SET_CLOCK_ID_SWR_NPL_CLK; msm_sdw->sdw_npl_clk.clk_freq_in_hz = SDW_NPL_FREQ; msm_sdw->sdw_npl_clk.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; msm_sdw->sdw_npl_clk.clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT; msm_sdw->sdw_npl_clk.enable = 0; INIT_DELAYED_WORK(&msm_sdw->disable_int_mclk1_work, msm_disable_int_mclk1); mutex_init(&msm_sdw->cdc_int_mclk1_mutex); mutex_init(&msm_sdw->sdw_npl_clk_mutex); mutex_init(&msm_sdw->io_lock); mutex_init(&msm_sdw->sdw_read_lock); mutex_init(&msm_sdw->sdw_write_lock); mutex_init(&msm_sdw->sdw_clk_lock); mutex_init(&msm_sdw->codec_mutex); schedule_work(&msm_sdw->msm_sdw_add_child_devices_work); dev_dbg(&pdev->dev, "%s: msm_sdw driver probe done\n", __func__); return ret; err_sdw_cdc: devm_kfree(&pdev->dev, msm_sdw); return ret; } static int msm_sdw_remove(struct platform_device *pdev) { struct msm_sdw_priv *msm_sdw; msm_sdw = dev_get_drvdata(&pdev->dev); mutex_destroy(&msm_sdw->io_lock); mutex_destroy(&msm_sdw->sdw_read_lock); mutex_destroy(&msm_sdw->sdw_write_lock); mutex_destroy(&msm_sdw->sdw_clk_lock); mutex_destroy(&msm_sdw->codec_mutex); mutex_destroy(&msm_sdw->cdc_int_mclk1_mutex); devm_kfree(&pdev->dev, msm_sdw); snd_soc_unregister_codec(&pdev->dev); return 0; } static const struct of_device_id msm_sdw_codec_dt_match[] = { { .compatible = "qcom,msm-sdw-codec", }, {} }; static struct platform_driver msm_sdw_codec_driver = { .probe = msm_sdw_probe, .remove = msm_sdw_remove, .driver = { .name = "msm_sdw_codec", .owner = THIS_MODULE, .of_match_table = msm_sdw_codec_dt_match, }, }; module_platform_driver(msm_sdw_codec_driver); MODULE_DESCRIPTION("MSM Soundwire Codec driver"); MODULE_LICENSE("GPL v2");