diff options
author | Takashi Iwai <tiwai@suse.de> | 2018-08-13 12:12:31 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2018-08-13 12:12:31 +0200 |
commit | f5b6c1fcb42fe7d6f2f6eb2220512e2a5f875133 (patch) | |
tree | 325f29d9788e80a0dd66d907ce38650834060e4b /sound/soc | |
parent | 73b383141d296c55bfbc0ce336a4a946627e7780 (diff) | |
parent | 4aa5db22d35588e1a5d2ee88472348ea73d9fb23 (diff) | |
download | blackbird-op-linux-f5b6c1fcb42fe7d6f2f6eb2220512e2a5f875133.tar.gz blackbird-op-linux-f5b6c1fcb42fe7d6f2f6eb2220512e2a5f875133.zip |
Merge tag 'asoc-v4.19' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.19
A fairly big update, including quite a bit of core activity this time
around (which is good to see) along with a fairly large set of new
drivers.
- A new snd_pcm_stop_xrun() helper which is now used in several
drivers.
- Support for providing name prefixes to generic component nodes.
- Quite a few fixes for DPCM as it gains a bit wider use and more
robust testing.
- Generalization of the DIO2125 support to a simple amplifier driver.
- Accessory detection support for the audio graph card.
- DT support for PXA AC'97 devices.
- Quirks for a number of new x86 systems.
- Support for AM Logic Meson, Everest ES7154, Intel systems with
RT5682, Qualcomm QDSP6 and WCD9335, Realtek RT5682 and TI TAS5707.
Diffstat (limited to 'sound/soc')
222 files changed, 12968 insertions, 2457 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 41af6b9cc350..1cf11cf51e1d 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -57,6 +57,7 @@ source "sound/soc/kirkwood/Kconfig" source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mediatek/Kconfig" +source "sound/soc/meson/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 06389a5385d7..62a5f87c3cfc 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mediatek/ +obj-$(CONFIG_SND_SOC) += meson/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 6cbf9cf4d1a4..58c1dcb4d255 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -8,6 +8,7 @@ config SND_SOC_AMD_CZ_DA7219MX98357_MACH select SND_SOC_DA7219 select SND_SOC_MAX98357A select SND_SOC_ADAU7002 + select REGULATOR depends on SND_SOC_AMD_ACP && I2C help This option enables machine driver for DA7219 and MAX9835. diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index ccddc6650b9c..8e3275a96a82 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -32,6 +32,8 @@ #include <linux/clk.h> #include <linux/gpio.h> #include <linux/module.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/driver.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/acpi.h> @@ -148,7 +150,8 @@ static int cz_da7219_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - machine->i2s_instance = I2S_BT_INSTANCE; + machine->i2s_instance = I2S_SP_INSTANCE; + machine->capture_channel = CAP_CHANNEL1; return da7219_clk_enable(substream); } @@ -163,7 +166,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream) struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); - machine->i2s_instance = I2S_SP_INSTANCE; + machine->i2s_instance = I2S_BT_INSTANCE; return da7219_clk_enable(substream); } @@ -172,13 +175,24 @@ static void cz_max_shutdown(struct snd_pcm_substream *substream) da7219_clk_disable(); } -static int cz_dmic_startup(struct snd_pcm_substream *substream) +static int cz_dmic0_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->i2s_instance = I2S_BT_INSTANCE; + return da7219_clk_enable(substream); +} + +static int cz_dmic1_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); machine->i2s_instance = I2S_SP_INSTANCE; + machine->capture_channel = CAP_CHANNEL0; return da7219_clk_enable(substream); } @@ -197,23 +211,39 @@ static const struct snd_soc_ops cz_max_play_ops = { .shutdown = cz_max_shutdown, }; -static const struct snd_soc_ops cz_dmic_cap_ops = { - .startup = cz_dmic_startup, +static const struct snd_soc_ops cz_dmic0_cap_ops = { + .startup = cz_dmic0_startup, + .shutdown = cz_dmic_shutdown, +}; + +static const struct snd_soc_ops cz_dmic1_cap_ops = { + .startup = cz_dmic1_startup, .shutdown = cz_dmic_shutdown, }; static struct snd_soc_dai_link cz_dai_7219_98357[] = { { - .name = "amd-da7219-play-cap", - .stream_name = "Playback and Capture", + .name = "amd-da7219-play", + .stream_name = "Playback", .platform_name = "acp_audio_dma.0.auto", - .cpu_dai_name = "designware-i2s.3.auto", + .cpu_dai_name = "designware-i2s.1.auto", .codec_dai_name = "da7219-hifi", .codec_name = "i2c-DLGS7219:00", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .init = cz_da7219_init, .dpcm_playback = 1, + .ops = &cz_da7219_cap_ops, + }, + { + .name = "amd-da7219-cap", + .stream_name = "Capture", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.2.auto", + .codec_dai_name = "da7219-hifi", + .codec_name = "i2c-DLGS7219:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, .dpcm_capture = 1, .ops = &cz_da7219_cap_ops, }, @@ -221,7 +251,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .platform_name = "acp_audio_dma.0.auto", - .cpu_dai_name = "designware-i2s.1.auto", + .cpu_dai_name = "designware-i2s.3.auto", .codec_dai_name = "HiFi", .codec_name = "MX98357A:00", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF @@ -230,8 +260,22 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .ops = &cz_max_play_ops, }, { - .name = "dmic", - .stream_name = "DMIC Capture", + /* C panel DMIC */ + .name = "dmic0", + .stream_name = "DMIC0 Capture", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.3.auto", + .codec_dai_name = "adau7002-hifi", + .codec_name = "ADAU7002:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .dpcm_capture = 1, + .ops = &cz_dmic0_cap_ops, + }, + { + /* A/B panel DMIC */ + .name = "dmic1", + .stream_name = "DMIC1 Capture", .platform_name = "acp_audio_dma.0.auto", .cpu_dai_name = "designware-i2s.2.auto", .codec_dai_name = "adau7002-hifi", @@ -239,7 +283,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .dpcm_capture = 1, - .ops = &cz_dmic_cap_ops, + .ops = &cz_dmic1_cap_ops, }, }; @@ -278,11 +322,52 @@ static struct snd_soc_card cz_card = { .num_controls = ARRAY_SIZE(cz_mc_controls), }; +static struct regulator_consumer_supply acp_da7219_supplies[] = { + REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"), + REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"), + REGULATOR_SUPPLY("VDDIO", "i2c-DLGS7219:00"), + REGULATOR_SUPPLY("IOVDD", "ADAU7002:00"), +}; + +static struct regulator_init_data acp_da7219_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(acp_da7219_supplies), + .consumer_supplies = acp_da7219_supplies, +}; + +static struct regulator_config acp_da7219_cfg = { + .init_data = &acp_da7219_data, +}; + +static struct regulator_ops acp_da7219_ops = { +}; + +static struct regulator_desc acp_da7219_desc = { + .name = "reg-fixed-1.8V", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &acp_da7219_ops, + .fixed_uV = 1800000, /* 1.8V */ + .n_voltages = 1, +}; + static int cz_probe(struct platform_device *pdev) { int ret; struct snd_soc_card *card; struct acp_platform_info *machine; + struct regulator_dev *rdev; + + acp_da7219_cfg.dev = &pdev->dev; + rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc, + &acp_da7219_cfg); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator: %d\n", + (int)PTR_ERR(rdev)); + return -EINVAL; + } machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), GFP_KERNEL); diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 77203841c535..e359938e3d7e 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -224,13 +224,11 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, switch (asic_type) { case CHIP_STONEY: dmadscr[i].xfer_val |= - BIT(22) | (ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC << 16) | (size / 2); break; default: dmadscr[i].xfer_val |= - BIT(22) | (ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION << 16) | (size / 2); } @@ -322,22 +320,87 @@ static void config_acp_dma(void __iomem *acp_mmio, struct audio_substream_data *rtd, u32 asic_type) { + u16 ch_acp_sysmem, ch_acp_i2s; + acp_pte_config(acp_mmio, rtd->pg, rtd->num_of_pages, rtd->pte_offset); + + if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) { + ch_acp_sysmem = rtd->ch1; + ch_acp_i2s = rtd->ch2; + } else { + ch_acp_i2s = rtd->ch1; + ch_acp_sysmem = rtd->ch2; + } /* Configure System memory <-> ACP SRAM DMA descriptors */ set_acp_sysmem_dma_descriptors(acp_mmio, rtd->size, rtd->direction, rtd->pte_offset, - rtd->ch1, rtd->sram_bank, + ch_acp_sysmem, rtd->sram_bank, rtd->dma_dscr_idx_1, asic_type); /* Configure ACP SRAM <-> I2S DMA descriptors */ set_acp_to_i2s_dma_descriptors(acp_mmio, rtd->size, rtd->direction, rtd->sram_bank, - rtd->destination, rtd->ch2, + rtd->destination, ch_acp_i2s, rtd->dma_dscr_idx_2, asic_type); } +static void acp_dma_cap_channel_enable(void __iomem *acp_mmio, + u16 cap_channel) +{ + u32 val, ch_reg, imr_reg, res_reg; + + switch (cap_channel) { + case CAP_CHANNEL1: + ch_reg = mmACP_I2SMICSP_RER1; + res_reg = mmACP_I2SMICSP_RCR1; + imr_reg = mmACP_I2SMICSP_IMR1; + break; + case CAP_CHANNEL0: + default: + ch_reg = mmACP_I2SMICSP_RER0; + res_reg = mmACP_I2SMICSP_RCR0; + imr_reg = mmACP_I2SMICSP_IMR0; + break; + } + val = acp_reg_read(acp_mmio, + mmACP_I2S_16BIT_RESOLUTION_EN); + if (val & ACP_I2S_MIC_16BIT_RESOLUTION_EN) { + acp_reg_write(0x0, acp_mmio, ch_reg); + /* Set 16bit resolution on capture */ + acp_reg_write(0x2, acp_mmio, res_reg); + } + val = acp_reg_read(acp_mmio, imr_reg); + val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK; + val &= ~ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK; + acp_reg_write(val, acp_mmio, imr_reg); + acp_reg_write(0x1, acp_mmio, ch_reg); +} + +static void acp_dma_cap_channel_disable(void __iomem *acp_mmio, + u16 cap_channel) +{ + u32 val, ch_reg, imr_reg; + + switch (cap_channel) { + case CAP_CHANNEL1: + imr_reg = mmACP_I2SMICSP_IMR1; + ch_reg = mmACP_I2SMICSP_RER1; + break; + case CAP_CHANNEL0: + default: + imr_reg = mmACP_I2SMICSP_IMR0; + ch_reg = mmACP_I2SMICSP_RER0; + break; + } + val = acp_reg_read(acp_mmio, imr_reg); + val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXDAM_MASK; + val |= ACP_I2SMICSP_IMR1__I2SMICSP_RXFOM_MASK; + acp_reg_write(val, acp_mmio, imr_reg); + acp_reg_write(0x0, acp_mmio, ch_reg); +} + /* Start a given DMA channel transfer */ -static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) +static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num, bool is_circular) { u32 dma_ctrl; @@ -356,10 +419,8 @@ static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) switch (ch_num) { case ACP_TO_I2S_DMA_CH_NUM: - case ACP_TO_SYSRAM_CH_NUM: case I2S_TO_ACP_DMA_CH_NUM: case ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM: - case ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM: case I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM: dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK; break; @@ -368,8 +429,11 @@ static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) break; } - /* circular for both DMA channel */ - dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK; + /* enable for ACP to SRAM DMA channel */ + if (is_circular == true) + dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK; + else + dma_ctrl &= ~ACP_DMA_CNTL_0__Circular_DMA_En_MASK; acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num); } @@ -613,6 +677,7 @@ static int acp_deinit(void __iomem *acp_mmio) /* ACP DMA irq handler routine for playback, capture usecases */ static irqreturn_t dma_irq_handler(int irq, void *arg) { + u16 dscr_idx; u32 intr_flag, ext_intr_status; struct audio_drv_data *irq_data; void __iomem *acp_mmio; @@ -644,32 +709,39 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) { valid_irq = true; + if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_14) == + CAPTURE_START_DMA_DESCR_CH15) + dscr_idx = CAPTURE_END_DMA_DESCR_CH14; + else + dscr_idx = CAPTURE_START_DMA_DESCR_CH14; + config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM, dscr_idx, + 1, 0); + acp_dma_start(acp_mmio, ACP_TO_SYSRAM_CH_NUM, false); + snd_pcm_period_elapsed(irq_data->capture_i2ssp_stream); acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) << 16, acp_mmio, mmACP_EXTERNAL_INTR_STAT); } - if ((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) != 0) { - valid_irq = true; - acp_reg_write((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); - } - if ((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) != 0) { valid_irq = true; + if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_10) == + CAPTURE_START_DMA_DESCR_CH11) + dscr_idx = CAPTURE_END_DMA_DESCR_CH10; + else + dscr_idx = CAPTURE_START_DMA_DESCR_CH10; + config_acp_dma_channel(acp_mmio, + ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM, + dscr_idx, 1, 0); + acp_dma_start(acp_mmio, ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM, + false); + snd_pcm_period_elapsed(irq_data->capture_i2sbt_stream); acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) << 16, acp_mmio, mmACP_EXTERNAL_INTR_STAT); } - if ((intr_flag & BIT(ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM)) != 0) { - valid_irq = true; - acp_reg_write((intr_flag & - BIT(ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); - } - if (valid_irq) return IRQ_HANDLED; else @@ -773,7 +845,10 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, if (WARN_ON(!rtd)) return -EINVAL; - rtd->i2s_instance = pinfo->i2s_instance; + if (pinfo) { + rtd->i2s_instance = pinfo->i2s_instance; + rtd->capture_channel = pinfo->capture_channel; + } if (adata->asic_type == CHIP_STONEY) { val = acp_reg_read(adata->acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); @@ -841,8 +916,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, switch (rtd->i2s_instance) { case I2S_BT_INSTANCE: rtd->pte_offset = ACP_ST_BT_CAPTURE_PTE_OFFSET; - rtd->ch1 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM; - rtd->ch2 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM; + rtd->ch1 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM; + rtd->ch2 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM; rtd->sram_bank = ACP_SRAM_BANK_4_ADDRESS; rtd->destination = FROM_BLUETOOTH; rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH10; @@ -851,13 +926,14 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH; rtd->byte_cnt_low_reg_offset = mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW; + rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_11; adata->capture_i2sbt_stream = substream; break; case I2S_SP_INSTANCE: default: rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET; - rtd->ch1 = ACP_TO_SYSRAM_CH_NUM; - rtd->ch2 = I2S_TO_ACP_DMA_CH_NUM; + rtd->ch1 = I2S_TO_ACP_DMA_CH_NUM; + rtd->ch2 = ACP_TO_SYSRAM_CH_NUM; switch (adata->asic_type) { case CHIP_STONEY: rtd->pte_offset = ACP_ST_CAPTURE_PTE_OFFSET; @@ -874,6 +950,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, mmACP_I2S_RECEIVED_BYTE_CNT_HIGH; rtd->byte_cnt_low_reg_offset = mmACP_I2S_RECEIVED_BYTE_CNT_LOW; + rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_15; adata->capture_i2ssp_stream = substream; } } @@ -927,6 +1004,8 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) u32 buffersize; u32 pos = 0; u64 bytescount = 0; + u16 dscr; + u32 period_bytes, delay; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; @@ -934,12 +1013,25 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) if (!rtd) return -EINVAL; - buffersize = frames_to_bytes(runtime, runtime->buffer_size); - bytescount = acp_get_byte_count(rtd); - - if (bytescount > rtd->bytescount) - bytescount -= rtd->bytescount; - pos = do_div(bytescount, buffersize); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + period_bytes = frames_to_bytes(runtime, runtime->period_size); + dscr = acp_reg_read(rtd->acp_mmio, rtd->dma_curr_dscr); + if (dscr == rtd->dma_dscr_idx_1) + pos = period_bytes; + else + pos = 0; + bytescount = acp_get_byte_count(rtd); + if (bytescount > rtd->bytescount) + bytescount -= rtd->bytescount; + delay = do_div(bytescount, period_bytes); + runtime->delay = bytes_to_frames(runtime, delay); + } else { + buffersize = frames_to_bytes(runtime, runtime->buffer_size); + bytescount = acp_get_byte_count(rtd); + if (bytescount > rtd->bytescount) + bytescount -= rtd->bytescount; + pos = do_div(bytescount, buffersize); + } return bytes_to_frames(runtime, pos); } @@ -953,16 +1045,24 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + u16 ch_acp_sysmem, ch_acp_i2s; if (!rtd) return -EINVAL; + if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) { + ch_acp_sysmem = rtd->ch1; + ch_acp_i2s = rtd->ch2; + } else { + ch_acp_i2s = rtd->ch1; + ch_acp_sysmem = rtd->ch2; + } config_acp_dma_channel(rtd->acp_mmio, - rtd->ch1, + ch_acp_sysmem, rtd->dma_dscr_idx_1, NUM_DSCRS_PER_CHANNEL, 0); config_acp_dma_channel(rtd->acp_mmio, - rtd->ch2, + ch_acp_i2s, rtd->dma_dscr_idx_2, NUM_DSCRS_PER_CHANNEL, 0); return 0; @@ -971,7 +1071,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) { int ret; - u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; @@ -982,37 +1081,32 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - bytescount = acp_get_byte_count(rtd); - if (rtd->bytescount == 0) - rtd->bytescount = bytescount; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - acp_dma_start(rtd->acp_mmio, rtd->ch1); - acp_dma_start(rtd->acp_mmio, rtd->ch2); + rtd->bytescount = acp_get_byte_count(rtd); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (rtd->capture_channel == CAP_CHANNEL0) { + acp_dma_cap_channel_disable(rtd->acp_mmio, + CAP_CHANNEL1); + acp_dma_cap_channel_enable(rtd->acp_mmio, + CAP_CHANNEL0); + } + if (rtd->capture_channel == CAP_CHANNEL1) { + acp_dma_cap_channel_disable(rtd->acp_mmio, + CAP_CHANNEL0); + acp_dma_cap_channel_enable(rtd->acp_mmio, + CAP_CHANNEL1); + } + acp_dma_start(rtd->acp_mmio, rtd->ch1, true); } else { - acp_dma_start(rtd->acp_mmio, rtd->ch2); - acp_dma_start(rtd->acp_mmio, rtd->ch1); + acp_dma_start(rtd->acp_mmio, rtd->ch1, true); + acp_dma_start(rtd->acp_mmio, rtd->ch2, true); } ret = 0; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - /* For playback, non circular dma should be stopped first - * i.e Sysram to acp dma transfer channel(rtd->ch1) should be - * stopped before stopping cirular dma which is acp sram to i2s - * fifo dma transfer channel(rtd->ch2). Where as in Capture - * scenario, i2s fifo to acp sram dma channel(rtd->ch2) stopped - * first before stopping acp sram to sysram which is circular - * dma(rtd->ch1). - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - acp_dma_stop(rtd->acp_mmio, rtd->ch1); - ret = acp_dma_stop(rtd->acp_mmio, rtd->ch2); - } else { - acp_dma_stop(rtd->acp_mmio, rtd->ch2); - ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1); - } - rtd->bytescount = 0; + acp_dma_stop(rtd->acp_mmio, rtd->ch2); + ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1); break; default: ret = -EINVAL; diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 9cd3e96c84d4..be3963e8f4fa 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -55,6 +55,8 @@ #define I2S_SP_INSTANCE 0x01 #define I2S_BT_INSTANCE 0x02 +#define CAP_CHANNEL0 0x00 +#define CAP_CHANNEL1 0x01 #define ACP_TILE_ON_MASK 0x03 #define ACP_TILE_OFF_MASK 0x02 @@ -72,16 +74,16 @@ #define ACP_TO_I2S_DMA_CH_NUM 13 /* Capture DMA channels */ -#define ACP_TO_SYSRAM_CH_NUM 14 -#define I2S_TO_ACP_DMA_CH_NUM 15 +#define I2S_TO_ACP_DMA_CH_NUM 14 +#define ACP_TO_SYSRAM_CH_NUM 15 /* Playback DMA Channels for I2S BT instance */ #define SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM 8 #define ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM 9 /* Capture DMA Channels for I2S BT Instance */ -#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 10 -#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 11 +#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 10 +#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 11 #define NUM_DSCRS_PER_CHANNEL 2 @@ -125,6 +127,7 @@ struct audio_substream_data { unsigned int order; u16 num_of_pages; u16 i2s_instance; + u16 capture_channel; u16 direction; u16 ch1; u16 ch2; @@ -135,6 +138,7 @@ struct audio_substream_data { u32 sram_bank; u32 byte_cnt_high_reg_offset; u32 byte_cnt_low_reg_offset; + u32 dma_curr_dscr; uint64_t size; u64 bytescount; void __iomem *acp_mmio; @@ -155,6 +159,7 @@ struct audio_drv_data { */ struct acp_platform_info { u16 i2s_instance; + u16 capture_channel; }; union acp_dma_count { diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 5d3b5af9fd92..d88c1d995036 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -206,7 +206,6 @@ struct atmel_i2s_dev { struct regmap *regmap; struct clk *pclk; struct clk *gclk; - struct clk *aclk; struct snd_dmaengine_dai_dma_data playback; struct snd_dmaengine_dai_dma_data capture; unsigned int fmt; @@ -303,7 +302,7 @@ static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs) { int i, best; - if (!dev->gclk || !dev->aclk) { + if (!dev->gclk) { dev_err(dev->dev, "cannot generate the I2S Master Clock\n"); return -EINVAL; } @@ -421,7 +420,7 @@ static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev, bool enabled) { unsigned int mr, mr_mask; - unsigned long aclk_rate; + unsigned long gclk_rate; int ret; mr = 0; @@ -445,35 +444,18 @@ static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev, /* Disable/unprepare the PMC generated clock. */ clk_disable_unprepare(dev->gclk); - /* Disable/unprepare the PLL audio clock. */ - clk_disable_unprepare(dev->aclk); return 0; } if (!dev->gck_param) return -EINVAL; - aclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1); + gclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1); - /* Fist change the PLL audio clock frequency ... */ - ret = clk_set_rate(dev->aclk, aclk_rate); + ret = clk_set_rate(dev->gclk, gclk_rate); if (ret) return ret; - /* - * ... then set the PMC generated clock rate to the very same frequency - * to set the gclk parent to aclk. - */ - ret = clk_set_rate(dev->gclk, aclk_rate); - if (ret) - return ret; - - /* Prepare and enable the PLL audio clock first ... */ - ret = clk_prepare_enable(dev->aclk); - if (ret) - return ret; - - /* ... then prepare and enable the PMC generated clock. */ ret = clk_prepare_enable(dev->gclk); if (ret) return ret; @@ -668,28 +650,14 @@ static int atmel_i2s_probe(struct platform_device *pdev) return err; } - /* Get audio clocks to generate the I2S Master Clock (I2S_MCK) */ - dev->aclk = devm_clk_get(&pdev->dev, "aclk"); + /* Get audio clock to generate the I2S Master Clock (I2S_MCK) */ dev->gclk = devm_clk_get(&pdev->dev, "gclk"); - if (IS_ERR(dev->aclk) && IS_ERR(dev->gclk)) { - if (PTR_ERR(dev->aclk) == -EPROBE_DEFER || - PTR_ERR(dev->gclk) == -EPROBE_DEFER) + if (IS_ERR(dev->gclk)) { + if (PTR_ERR(dev->gclk) == -EPROBE_DEFER) return -EPROBE_DEFER; /* Master Mode not supported */ - dev->aclk = NULL; dev->gclk = NULL; - } else if (IS_ERR(dev->gclk)) { - err = PTR_ERR(dev->gclk); - dev_err(&pdev->dev, - "failed to get the PMC generated clock: %d\n", err); - return err; - } else if (IS_ERR(dev->aclk)) { - err = PTR_ERR(dev->aclk); - dev_err(&pdev->dev, - "failed to get the PLL audio clock: %d\n", err); - return err; } - dev->dev = &pdev->dev; dev->regmap = regmap; platform_set_drvdata(pdev, dev); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 63cf62e9c9aa..efb095dbcd71 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -74,12 +74,12 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C - select SND_SOC_DIO2125 select SND_SOC_DMIC if GPIOLIB select SND_SOC_ES8316 if I2C select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C select SND_SOC_ES7134 + select SND_SOC_ES7241 select SND_SOC_GTM601 select SND_SOC_HDAC_HDMI select SND_SOC_ICS43432 @@ -141,8 +141,10 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5668 if I2C select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C && SPI_MASTER + select SND_SOC_RT5682 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE + select SND_SOC_SIMPLE_AMPLIFIER select SND_SOC_SIRF_AUDIO_CODEC select SND_SOC_SPDIF select SND_SOC_SSM2305 @@ -572,10 +574,6 @@ config SND_SOC_DA732X config SND_SOC_DA9055 tristate -config SND_SOC_DIO2125 - tristate "Dioo DIO2125 Amplifier" - select GPIOLIB - config SND_SOC_DMIC tristate @@ -588,6 +586,9 @@ config SND_SOC_HDMI_CODEC config SND_SOC_ES7134 tristate "Everest Semi ES7134 CODEC" +config SND_SOC_ES7241 + tristate "Everest Semi ES7241 CODEC" + config SND_SOC_ES8316 tristate "Everest Semi ES8316 CODEC" depends on I2C @@ -778,6 +779,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5668=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default y if SND_SOC_RT5682=y default y if SND_SOC_RT1305=y default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m @@ -791,6 +793,7 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5668=m default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m + default m if SND_SOC_RT5682=m default m if SND_SOC_RT1305=m config SND_SOC_RL6347A @@ -871,6 +874,9 @@ config SND_SOC_RT5677_SPI tristate default SND_SOC_RT5677 && SPI +config SND_SOC_RT5682 + tristate + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" @@ -891,6 +897,10 @@ config SND_SOC_SIGMADSP_REGMAP tristate select SND_SOC_SIGMADSP +config SND_SOC_SIMPLE_AMPLIFIER + tristate "Simple Audio Amplifier" + select GPIOLIB + config SND_SOC_SIRF_AUDIO_CODEC tristate "SiRF SoC internal audio codec" select REGMAP_MMIO @@ -953,8 +963,11 @@ config SND_SOC_TAS5086 depends on I2C config SND_SOC_TAS571X - tristate "Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 power amplifiers" + tristate "Texas Instruments TAS571x power amplifiers" depends on I2C + help + Enable support for Texas Instruments TAS5707, TAS5711, TAS5717, + TAS5719 and TAS5721 power amplifiers config SND_SOC_TAS5720 tristate "Texas Instruments TAS5720 Mono Audio amplifier" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e023fdf85221..7ae7c85e8219 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -71,6 +71,7 @@ snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-dmic-objs := dmic.o snd-soc-es7134-objs := es7134.o +snd-soc-es7241-objs := es7241.o snd-soc-es8316-objs := es8316.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o @@ -146,6 +147,7 @@ snd-soc-rt5668-objs := rt5668.o snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o +snd-soc-rt5682-objs := rt5682.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -249,9 +251,9 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-zx-aud96p22-objs := zx_aud96p22.o # Amp -snd-soc-dio2125-objs := dio2125.o snd-soc-max9877-objs := max9877.o snd-soc-max98504-objs := max98504.o +snd-soc-simple-amplifier-objs := simple-amplifier.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tas2552-objs := tas2552.o @@ -329,6 +331,7 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o +obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o @@ -405,6 +408,7 @@ obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o +obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o @@ -507,7 +511,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o # Amp -obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o +obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index ae41edd1c406..57169b8ff14e 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -299,6 +299,7 @@ static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = { { "DSP", NULL, "Left Decimator" }, { "DSP", NULL, "Right Decimator" }, + { "DSP", NULL, "Playback" }, }; static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = { diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index db21ecbe0762..8b9ca7e7a682 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -648,6 +648,7 @@ static int adav80x_set_pll(struct snd_soc_component *component, int pll_id, pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV; break; } + /* fall through */ default: return -EINVAL; } diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 31ec0ba2e639..299ada4dfaa0 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -558,7 +558,7 @@ static int __maybe_unused ak4458_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ -struct snd_soc_component_driver soc_codec_dev_ak4458 = { +static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .probe = ak4458_probe, .remove = ak4458_remove, .controls = ak4458_snd_controls, diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index b7ee13406d93..2fa83a1a84cf 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -1,13 +1,8 @@ -/* - * ak4554.c - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// ak4554.c +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> #include <linux/module.h> #include <sound/soc.h> @@ -97,6 +92,6 @@ static struct platform_driver ak4554_driver = { }; module_platform_driver(ak4554_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SoC AK4554 driver"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 8523ff9351cf..c1181a20714d 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -1,18 +1,14 @@ -/* - * ak4613.c -- Asahi Kasei ALSA Soc Audio driver - * - * Copyright (C) 2015 Renesas Electronics Corporation - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * Based on ak4642.c by Kuninori Morimoto - * Based on wm8731.c by Richard Purdie - * Based on ak4535.c by Richard Purdie - * Based on wm8753.c by Liam Girdwood - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ak4613.c -- Asahi Kasei ALSA Soc Audio driver +// +// Copyright (C) 2015 Renesas Electronics Corporation +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// Based on ak4642.c by Kuninori Morimoto +// Based on wm8731.c by Richard Purdie +// Based on ak4535.c by Richard Purdie +// Based on wm8753.c by Liam Girdwood #include <linux/clk.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 605055964529..353237025514 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -1,17 +1,13 @@ -/* - * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Based on wm8731.c by Richard Purdie - * Based on ak4535.c by Richard Purdie - * Based on wm8753.c by Liam Girdwood - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Kuninori Morimoto <morimoto.kuninori@renesas.com> +// +// Based on wm8731.c by Richard Purdie +// Based on ak4535.c by Richard Purdie +// Based on wm8753.c by Liam Girdwood /* ** CAUTION ** * @@ -709,4 +705,4 @@ module_i2c_driver(ak4642_i2c_driver); MODULE_DESCRIPTION("Soc AK4642 driver"); MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index f4ed5cc40661..448bb90c9c8e 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -322,13 +322,13 @@ static int __maybe_unused ak5558_runtime_resume(struct device *dev) return regcache_sync(ak5558->regmap); } -const struct dev_pm_ops ak5558_pm = { +static const struct dev_pm_ops ak5558_pm = { SET_RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; -struct snd_soc_component_driver soc_codec_dev_ak5558 = { +static const struct snd_soc_component_driver soc_codec_dev_ak5558 = { .probe = ak5558_probe, .remove = ak5558_remove, .controls = ak5558_snd_controls, diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 2a7a4168c072..3c266eeb89bf 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -219,7 +219,7 @@ static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg) { /* Unreadable registers are considered volatile */ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) - return 1; + return true; return reg == CS4270_CHIPID; } diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 196e9c343aeb..45e50fe3bf25 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -235,6 +235,9 @@ ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -1283,6 +1286,12 @@ static int cs47l24_probe(struct platform_device *pdev) return ret; } + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -1306,6 +1315,7 @@ static int cs47l24_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); return ret; @@ -1323,6 +1333,7 @@ static int cs47l24_remove(struct platform_device *pdev) arizona_free_spk_irqs(arizona); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); return 0; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 07dd33b09596..ab174b5114dc 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -362,8 +362,27 @@ static int cx20442_component_probe(struct snd_soc_component *component) return -ENOMEM; cx20442->por = regulator_get(component->dev, "POR"); - if (IS_ERR(cx20442->por)) - dev_warn(component->dev, "failed to get the regulator"); + if (IS_ERR(cx20442->por)) { + int err = PTR_ERR(cx20442->por); + + dev_warn(component->dev, "failed to get POR supply (%d)", err); + /* + * When running on a non-dt platform and requested regulator + * is not available, regulator_get() never returns + * -EPROBE_DEFER as it is not able to justify if the regulator + * may still appear later. On the other hand, the board can + * still set full constraints flag at late_initcall in order + * to instruct regulator_get() to return a dummy one if + * sufficient. Hence, if we get -ENODEV here, let's convert + * it to -EPROBE_DEFER and wait for the board to decide or + * let Deferred Probe infrastructure handle this error. + */ + if (err == -ENODEV) + err = -EPROBE_DEFER; + kfree(cx20442); + return err; + } + cx20442->tty = NULL; snd_soc_component_set_drvdata(component, cx20442); diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index a664111b7184..e172913d04a4 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1,19 +1,14 @@ -/* - * DA7210 ALSA Soc codec driver - * - * Copyright (c) 2009 Dialog Semiconductor - * Written by David Chen <Dajun.chen@diasemi.com> - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// DA7210 ALSA Soc codec driver +// +// Copyright (c) 2009 Dialog Semiconductor +// Written by David Chen <Dajun.chen@diasemi.com> +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com> +// +// Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S #include <linux/delay.h> #include <linux/i2c.h> diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 54cb5f24969f..92d006a5283e 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1140,9 +1140,9 @@ static bool da7213_volatile_register(struct device *dev, unsigned int reg) case DA7213_ALC_OFFSET_AUTO_M_R: case DA7213_ALC_OFFSET_AUTO_U_R: case DA7213_ALC_CIC_OP_LVL_DATA: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index a49ab751a036..2c7d5088e6f2 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -59,6 +59,7 @@ static void da7219_aad_btn_det_work(struct work_struct *work) container_of(work, struct da7219_aad_priv, btn_det_work); struct snd_soc_component *component = da7219_aad->component; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); u8 statusa, micbias_ctrl; bool micbias_up = false; int retries = 0; @@ -86,6 +87,8 @@ static void da7219_aad_btn_det_work(struct work_struct *work) if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) dev_warn(component->dev, "Mic bias status check timed out"); + da7219->micbias_on_event = true; + /* * Mic bias pulse required to enable mic, must be done before enabling * button detection to prevent erroneous button readings. @@ -439,6 +442,8 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data) snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, DA7219_BUTTON_CONFIG_MASK, 0); + da7219->micbias_on_event = false; + /* Disable mic bias */ snd_soc_dapm_disable_pin(dapm, "Mic Bias"); snd_soc_dapm_sync(dapm); diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 980a6a8bf56d..e46e9f4bc994 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -768,6 +768,30 @@ static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = { * DAPM Events */ +static int da7219_mic_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (da7219->micbias_on_event) { + /* + * Delay only for first capture after bias enabled to + * avoid possible DC offset related noise. + */ + da7219->micbias_on_event = false; + msleep(da7219->mic_pga_delay); + } + break; + default: + break; + } + + return 0; +} + static int da7219_dai_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -937,12 +961,12 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC"), /* Input PGAs */ - SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL, - DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, - NULL, 0), - SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL, - DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, - NULL, 0), + SND_SOC_DAPM_PGA_E("Mic PGA", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mic_pga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("Mixin PGA", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_settling_event, SND_SOC_DAPM_POST_PMU), /* Input Filters */ SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT, @@ -1847,6 +1871,14 @@ static void da7219_handle_pdata(struct snd_soc_component *component) snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_lvl); + /* + * Calculate delay required to compensate for DC offset in + * Mic PGA, based on Mic Bias voltage. + */ + da7219->mic_pga_delay = DA7219_MIC_PGA_BASE_DELAY + + (pdata->micbias_lvl * + DA7219_MIC_PGA_OFFSET_DELAY); + /* Mic */ switch (pdata->mic_amp_in_sel) { case DA7219_MIC_AMP_IN_SEL_DIFF: @@ -2143,9 +2175,9 @@ static bool da7219_volatile_register(struct device *dev, unsigned int reg) case DA7219_ACCDET_IRQ_EVENT_B: case DA7219_ACCDET_CONFIG_8: case DA7219_SYSTEM_STATUS: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 1b00023e33cd..3a006862f0e7 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -781,8 +781,10 @@ #define DA7219_SYS_STAT_CHECK_DELAY 50 /* Power up/down Delays */ -#define DA7219_SETTLING_DELAY 40 -#define DA7219_MIN_GAIN_DELAY 30 +#define DA7219_SETTLING_DELAY 40 +#define DA7219_MIN_GAIN_DELAY 30 +#define DA7219_MIC_PGA_BASE_DELAY 100 +#define DA7219_MIC_PGA_OFFSET_DELAY 40 enum da7219_clk_src { DA7219_CLKSRC_MCLK = 0, @@ -828,6 +830,8 @@ struct da7219_priv { bool master; bool alc_en; + bool micbias_on_event; + unsigned int mic_pga_delay; u8 gain_ramp_ctrl; }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index afdf90c78884..f6a7bf9560e7 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1041,9 +1041,9 @@ static bool da9055_volatile_register(struct device *dev, case DA9055_HP_R_GAIN_STATUS: case DA9055_LINE_GAIN_STATUS: case DA9055_ALC_CIC_OP_LVL_DATA: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c index 58515bb1a303..6d7bca7b78ca 100644 --- a/sound/soc/codecs/es7134.c +++ b/sound/soc/codecs/es7134.c @@ -17,6 +17,7 @@ * in the file called COPYING. */ +#include <linux/of_platform.h> #include <linux/module.h> #include <sound/soc.h> @@ -24,6 +25,82 @@ * The everest 7134 is a very simple DA converter with no register */ +struct es7134_clock_mode { + unsigned int rate_min; + unsigned int rate_max; + unsigned int *mclk_fs; + unsigned int mclk_fs_num; +}; + +struct es7134_chip { + struct snd_soc_dai_driver *dai_drv; + const struct es7134_clock_mode *modes; + unsigned int mode_num; + const struct snd_soc_dapm_widget *extra_widgets; + unsigned int extra_widget_num; + const struct snd_soc_dapm_route *extra_routes; + unsigned int extra_route_num; +}; + +struct es7134_data { + unsigned int mclk; + const struct es7134_chip *chip; +}; + +static int es7134_check_mclk(struct snd_soc_dai *dai, + struct es7134_data *priv, + unsigned int rate) +{ + unsigned int mfs = priv->mclk / rate; + int i, j; + + for (i = 0; i < priv->chip->mode_num; i++) { + const struct es7134_clock_mode *mode = &priv->chip->modes[i]; + + if (rate < mode->rate_min || rate > mode->rate_max) + continue; + + for (j = 0; j < mode->mclk_fs_num; j++) { + if (mode->mclk_fs[j] == mfs) + return 0; + } + + dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n", + mfs, rate); + return -EINVAL; + } + + /* should not happen */ + dev_err(dai->dev, "unsupported rate: %u\n", rate); + return -EINVAL; +} + +static int es7134_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct es7134_data *priv = snd_soc_dai_get_drvdata(dai); + + /* mclk has not been provided, assume it is OK */ + if (!priv->mclk) + return 0; + + return es7134_check_mclk(dai, priv, params_rate(params)); +} + +static int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct es7134_data *priv = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN && clk_id == 0) { + priv->mclk = freq; + return 0; + } + + return -ENOTSUPP; +} + static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | @@ -38,8 +115,38 @@ static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return 0; } +static int es7134_component_probe(struct snd_soc_component *c) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(c); + struct es7134_data *priv = snd_soc_component_get_drvdata(c); + const struct es7134_chip *chip = priv->chip; + int ret; + + if (chip->extra_widget_num) { + ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets, + chip->extra_widget_num); + if (ret) { + dev_err(c->dev, "failed to add extra widgets\n"); + return ret; + } + } + + if (chip->extra_route_num) { + ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes, + chip->extra_route_num); + if (ret) { + dev_err(c->dev, "failed to add extra routes\n"); + return ret; + } + } + + return 0; +} + static const struct snd_soc_dai_ops es7134_dai_ops = { .set_fmt = es7134_set_fmt, + .hw_params = es7134_hw_params, + .set_sysclk = es7134_set_sysclk, }; static struct snd_soc_dai_driver es7134_dai = { @@ -48,7 +155,11 @@ static struct snd_soc_dai_driver es7134_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | @@ -58,18 +169,56 @@ static struct snd_soc_dai_driver es7134_dai = { .ops = &es7134_dai_ops, }; +static const struct es7134_clock_mode es7134_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 }, + .mclk_fs_num = 5, + }, { + /* Double speed mode */ + .rate_min = 84000, + .rate_max = 100000, + .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 }, + .mclk_fs_num = 5, + }, { + /* Quad speed mode */ + .rate_min = 167000, + .rate_max = 192000, + .mclk_fs = (unsigned int[]) { 128, 192, 256 }, + .mclk_fs_num = 3, + }, +}; + +/* Digital I/O are also supplied by VDD on the es7134 */ +static const struct snd_soc_dapm_route es7134_extra_routes[] = { + { "Playback", NULL, "VDD", } +}; + +static const struct es7134_chip es7134_chip = { + .dai_drv = &es7134_dai, + .modes = es7134_modes, + .mode_num = ARRAY_SIZE(es7134_modes), + .extra_routes = es7134_extra_routes, + .extra_route_num = ARRAY_SIZE(es7134_extra_routes), +}; + static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("AOUTL"), SND_SOC_DAPM_OUTPUT("AOUTR"), SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0), }; static const struct snd_soc_dapm_route es7134_dapm_routes[] = { { "AOUTL", NULL, "DAC" }, { "AOUTR", NULL, "DAC" }, + { "DAC", NULL, "VDD" }, }; static const struct snd_soc_component_driver es7134_component_driver = { + .probe = es7134_component_probe, .dapm_widgets = es7134_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(es7134_dapm_widgets), .dapm_routes = es7134_dapm_routes, @@ -80,17 +229,87 @@ static const struct snd_soc_component_driver es7134_component_driver = { .non_legacy_dai_naming = 1, }; +static struct snd_soc_dai_driver es7154_dai = { + .name = "es7154-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S18_3LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7134_dai_ops, +}; + +static const struct es7134_clock_mode es7154_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256, + 384, 512, 768, 1024 }, + .mclk_fs_num = 9, + }, { + /* Double speed mode */ + .rate_min = 84000, + .rate_max = 100000, + .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512, + 768, 1024}, + .mclk_fs_num = 7, + } +}; + +/* Es7154 has a separate supply for digital I/O */ +static const struct snd_soc_dapm_widget es7154_extra_widgets[] = { + SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0), +}; + +static const struct snd_soc_dapm_route es7154_extra_routes[] = { + { "Playback", NULL, "PVDD", } +}; + +static const struct es7134_chip es7154_chip = { + .dai_drv = &es7154_dai, + .modes = es7154_modes, + .mode_num = ARRAY_SIZE(es7154_modes), + .extra_routes = es7154_extra_routes, + .extra_route_num = ARRAY_SIZE(es7154_extra_routes), + .extra_widgets = es7154_extra_widgets, + .extra_widget_num = ARRAY_SIZE(es7154_extra_widgets), +}; + static int es7134_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct es7134_data *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->chip = of_device_get_match_data(dev); + if (!priv->chip) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + return devm_snd_soc_register_component(&pdev->dev, &es7134_component_driver, - &es7134_dai, 1); + priv->chip->dai_drv, 1); } #ifdef CONFIG_OF static const struct of_device_id es7134_ids[] = { - { .compatible = "everest,es7134", }, - { .compatible = "everest,es7144", }, + { .compatible = "everest,es7134", .data = &es7134_chip }, + { .compatible = "everest,es7144", .data = &es7134_chip }, + { .compatible = "everest,es7154", .data = &es7154_chip }, { } }; MODULE_DEVICE_TABLE(of, es7134_ids); diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c new file mode 100644 index 000000000000..87991bd4acef --- /dev/null +++ b/sound/soc/codecs/es7241.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/gpio/consumer.h> +#include <linux/of_platform.h> +#include <linux/module.h> +#include <sound/soc.h> + +struct es7241_clock_mode { + unsigned int rate_min; + unsigned int rate_max; + unsigned int *slv_mfs; + unsigned int slv_mfs_num; + unsigned int mst_mfs; + unsigned int mst_m0:1; + unsigned int mst_m1:1; +}; + +struct es7241_chip { + const struct es7241_clock_mode *modes; + unsigned int mode_num; +}; + +struct es7241_data { + struct gpio_desc *reset; + struct gpio_desc *m0; + struct gpio_desc *m1; + unsigned int fmt; + unsigned int mclk; + bool is_slave; + const struct es7241_chip *chip; +}; + +static void es7241_set_mode(struct es7241_data *priv, int m0, int m1) +{ + /* put the device in reset */ + gpiod_set_value_cansleep(priv->reset, 0); + + /* set the mode */ + gpiod_set_value_cansleep(priv->m0, m0); + gpiod_set_value_cansleep(priv->m1, m1); + + /* take the device out of reset - datasheet does not specify a delay */ + gpiod_set_value_cansleep(priv->reset, 1); +} + +static int es7241_set_slave_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) +{ + int j; + + if (!mfs) + goto out_ok; + + for (j = 0; j < mode->slv_mfs_num; j++) { + if (mode->slv_mfs[j] == mfs) + goto out_ok; + } + + return -EINVAL; + +out_ok: + es7241_set_mode(priv, 1, 1); + return 0; +} + +static int es7241_set_master_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) +{ + /* + * We can't really set clock ratio, if the mclk/lrclk is different + * from what we provide, then error out + */ + if (mfs && mfs != mode->mst_mfs) + return -EINVAL; + + es7241_set_mode(priv, mode->mst_m0, mode->mst_m1); + + return 0; +} + +static int es7241_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int mfs = priv->mclk / rate; + int i; + + for (i = 0; i < priv->chip->mode_num; i++) { + const struct es7241_clock_mode *mode = &priv->chip->modes[i]; + + if (rate < mode->rate_min || rate >= mode->rate_max) + continue; + + if (priv->is_slave) + return es7241_set_slave_mode(priv, mode, mfs); + else + return es7241_set_master_mode(priv, mode, mfs); + } + + /* should not happen */ + dev_err(dai->dev, "unsupported rate: %u\n", rate); + return -EINVAL; +} + +static int es7241_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN && clk_id == 0) { + priv->mclk = freq; + return 0; + } + + return -ENOTSUPP; +} + +static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { + dev_err(dai->dev, "Unsupported dai clock inversion\n"); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != priv->fmt) { + dev_err(dai->dev, "Invalid dai format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + priv->is_slave = true; + break; + case SND_SOC_DAIFMT_CBM_CFM: + priv->is_slave = false; + break; + + default: + dev_err(dai->dev, "Unsupported clock configuration\n"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops es7241_dai_ops = { + .set_fmt = es7241_set_fmt, + .hw_params = es7241_hw_params, + .set_sysclk = es7241_set_sysclk, +}; + +static struct snd_soc_dai_driver es7241_dai = { + .name = "es7241-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7241_dai_ops, +}; + +static const struct es7241_clock_mode es7241_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .slv_mfs = (unsigned int[]) { 256, 384, 512, 768, 1024 }, + .slv_mfs_num = 5, + .mst_mfs = 256, + .mst_m0 = 0, + .mst_m1 = 0, + }, { + /* Double speed mode */ + .rate_min = 50000, + .rate_max = 100000, + .slv_mfs = (unsigned int[]) { 128, 192 }, + .slv_mfs_num = 2, + .mst_mfs = 128, + .mst_m0 = 1, + .mst_m1 = 0, + }, { + /* Quad speed mode */ + .rate_min = 100000, + .rate_max = 200000, + .slv_mfs = (unsigned int[]) { 64 }, + .slv_mfs_num = 1, + .mst_mfs = 64, + .mst_m0 = 0, + .mst_m1 = 1, + }, +}; + +static const struct es7241_chip es7241_chip = { + .modes = es7241_modes, + .mode_num = ARRAY_SIZE(es7241_modes), +}; + +static const struct snd_soc_dapm_widget es7241_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AINL"), + SND_SOC_DAPM_INPUT("AINR"), + SND_SOC_DAPM_DAC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDP", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDD", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDA", 0, 0), +}; + +static const struct snd_soc_dapm_route es7241_dapm_routes[] = { + { "ADC", NULL, "AINL", }, + { "ADC", NULL, "AINR", }, + { "ADC", NULL, "VDDA", }, + { "Capture", NULL, "VDDP", }, + { "Capture", NULL, "VDDD", }, +}; + +static const struct snd_soc_component_driver es7241_component_driver = { + .dapm_widgets = es7241_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es7241_dapm_widgets), + .dapm_routes = es7241_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es7241_dapm_routes), + .idle_bias_on = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static void es7241_parse_fmt(struct device *dev, struct es7241_data *priv) +{ + bool is_leftj; + + /* + * The format is given by a pull resistor on the SDOUT pin: + * pull-up for i2s, pull-down for left justified. + */ + is_leftj = of_property_read_bool(dev->of_node, + "everest,sdout-pull-down"); + if (is_leftj) + priv->fmt = SND_SOC_DAIFMT_LEFT_J; + else + priv->fmt = SND_SOC_DAIFMT_I2S; +} + +static int es7241_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct es7241_data *priv; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->chip = of_device_get_match_data(dev); + if (!priv->chip) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + es7241_parse_fmt(dev, priv); + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + err = PTR_ERR(priv->reset); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'reset' gpio: %d", err); + return err; + } + + priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW); + if (IS_ERR(priv->m0)) { + err = PTR_ERR(priv->m0); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'm0' gpio: %d", err); + return err; + } + + priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW); + if (IS_ERR(priv->m1)) { + err = PTR_ERR(priv->m1); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'm1' gpio: %d", err); + return err; + } + + return devm_snd_soc_register_component(&pdev->dev, + &es7241_component_driver, + &es7241_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id es7241_ids[] = { + { .compatible = "everest,es7241", .data = &es7241_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, es7241_ids); +#endif + +static struct platform_driver es7241_driver = { + .driver = { + .name = "es7241", + .of_match_table = of_match_ptr(es7241_ids), + }, + .probe = es7241_probe, +}; + +module_platform_driver(es7241_driver); + +MODULE_DESCRIPTION("ASoC ES7241 audio codec driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 4748a9d5de3b..7b8533abf637 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -2093,6 +2093,75 @@ static int hdac_hdmi_dev_remove(struct hdac_device *hdev) } #ifdef CONFIG_PM +/* + * Power management sequences + * ========================== + * + * The following explains the PM handling of HDAC HDMI with its parent + * device SKL and display power usage + * + * Probe + * ----- + * In SKL probe, + * 1. skl_probe_work() powers up the display (refcount++ -> 1) + * 2. enumerates the codecs on the link + * 3. powers down the display (refcount-- -> 0) + * + * In HDAC HDMI probe, + * 1. hdac_hdmi_dev_probe() powers up the display (refcount++ -> 1) + * 2. probe the codec + * 3. put the HDAC HDMI device to runtime suspend + * 4. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) + * + * Once children are runtime suspended, SKL device also goes to runtime + * suspend + * + * HDMI Playback + * ------------- + * Open HDMI device, + * 1. skl_runtime_resume() invoked + * 2. hdac_hdmi_runtime_resume() powers up the display (refcount++ -> 1) + * + * Close HDMI device, + * 1. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) + * 2. skl_runtime_suspend() invoked + * + * S0/S3 Cycle with playback in progress + * ------------------------------------- + * When the device is opened for playback, the device is runtime active + * already and the display refcount is 1 as explained above. + * + * Entering to S3, + * 1. hdmi_codec_prepare() invoke the runtime resume of codec which just + * increments the PM runtime usage count of the codec since the device + * is in use already + * 2. skl_suspend() powers down the display (refcount-- -> 0) + * + * Wakeup from S3, + * 1. skl_resume() powers up the display (refcount++ -> 1) + * 2. hdmi_codec_complete() invokes the runtime suspend of codec which just + * decrements the PM runtime usage count of the codec since the device + * is in use already + * + * Once playback is stopped, the display refcount is set to 0 as explained + * above in the HDMI playback sequence. The PM handlings are designed in + * such way that to balance the refcount of display power when the codec + * device put to S3 while playback is going on. + * + * S0/S3 Cycle without playback in progress + * ---------------------------------------- + * Entering to S3, + * 1. hdmi_codec_prepare() invoke the runtime resume of codec + * 2. skl_runtime_resume() invoked + * 3. hdac_hdmi_runtime_resume() powers up the display (refcount++ -> 1) + * 4. skl_suspend() powers down the display (refcount-- -> 0) + * + * Wakeup from S3, + * 1. skl_resume() powers up the display (refcount++ -> 1) + * 2. hdmi_codec_complete() invokes the runtime suspend of codec + * 3. hdac_hdmi_runtime_suspend() powers down the display (refcount-- -> 0) + * 4. skl_runtime_suspend() invoked + */ static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 38e4a8515709..d00734d31e04 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -291,10 +291,6 @@ static const struct snd_soc_dapm_widget hdmi_widgets[] = { SND_SOC_DAPM_OUTPUT("TX"), }; -static const struct snd_soc_dapm_route hdmi_routes[] = { - { "TX", NULL, "Playback" }, -}; - enum { DAI_ID_I2S = 0, DAI_ID_SPDIF, @@ -689,9 +685,23 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, return snd_ctl_add(rtd->card->snd_card, kctl); } +static int hdmi_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_route route = { + .sink = "TX", + .source = dai->driver->playback.stream_name, + }; + + dapm = snd_soc_component_get_dapm(dai->component); + + return snd_soc_dapm_add_routes(dapm, &route, 1); +} + static const struct snd_soc_dai_driver hdmi_i2s_dai = { .name = "i2s-hifi", .id = DAI_ID_I2S, + .probe = hdmi_dai_probe, .playback = { .stream_name = "I2S Playback", .channels_min = 2, @@ -707,6 +717,7 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = { static const struct snd_soc_dai_driver hdmi_spdif_dai = { .name = "spdif-hifi", .id = DAI_ID_SPDIF, + .probe = hdmi_dai_probe, .playback = { .stream_name = "SPDIF Playback", .channels_min = 2, @@ -733,8 +744,6 @@ static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, static const struct snd_soc_component_driver hdmi_driver = { .dapm_widgets = hdmi_widgets, .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), - .dapm_routes = hdmi_routes, - .num_dapm_routes = ARRAY_SIZE(hdmi_routes), .of_xlate_dai_id = hdmi_of_xlate_dai_id, .idle_bias_on = 1, .use_pmdown_time = 1, diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c index a92586106932..92b7125ea169 100644 --- a/sound/soc/codecs/max98373.c +++ b/sound/soc/codecs/max98373.c @@ -488,6 +488,7 @@ static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv, static bool max98373_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case MAX98373_R2000_SW_RESET: case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3: case MAX98373_R2010_IRQ_CTRL: case MAX98373_R2014_THERM_WARN_THRESH diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 74d7f52c7e73..6e6134589588 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -52,9 +52,9 @@ static bool max9850_volatile_register(struct device *dev, unsigned int reg) switch (reg) { case MAX9850_STATUSA: case MAX9850_STATUSB: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 17104f8dc1a9..e3c8cd17daf2 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -362,11 +362,8 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr) { - int osrate; - if (osr >= ARRAY_SIZE(osr_adc_sel)) return -EINVAL; - osrate = osr_adc_sel[osr].osr; if (rate * osr > CLK_ADC_MAX) { dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n"); diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 6bd14453f06e..468d5143e2c4 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -1274,7 +1274,7 @@ static int nau8824_calc_fll_param(unsigned int fll_in, fvco_max = 0; fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { - fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && fvco_max < fvco) { fvco_max = fvco; diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index dc6ea4987b7d..b9fed99d8b5e 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -2016,7 +2016,7 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, fvco_max = 0; fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { - fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && fvco_max < fvco) { fvco_max = fvco; diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c index 21f15219b3ad..8df6447c76a6 100644 --- a/sound/soc/codecs/pcm1789.c +++ b/sound/soc/codecs/pcm1789.c @@ -262,8 +262,7 @@ int pcm1789_common_exit(struct device *dev) { struct pcm1789_private *priv = dev_get_drvdata(dev); - if (&priv->work) - flush_work(&priv->work); + flush_work(&priv->work); return 0; } diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index 88fde70b1e9e..690c26e7389e 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -265,7 +265,7 @@ static int pcm186x_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); unsigned int rate = params_rate(params); - unsigned int format = params_format(params); + snd_pcm_format_t format = params_format(params); unsigned int width = params_width(params); unsigned int channels = params_channels(params); unsigned int div_lrck; diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index f4c8c45f4010..c4452efc7970 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -1066,7 +1066,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305) pr_debug("Left_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); pr_info("Left channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); - r0l = 562949953421312; + r0l = 562949953421312ULL; if (rhl != 0) do_div(r0l, rhl); pr_debug("Left_r0 = 0x%llx\n", r0l); @@ -1083,7 +1083,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305) pr_debug("Right_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); pr_info("Right channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); - r0r = 562949953421312; + r0r = 562949953421312ULL; if (rhl != 0) do_div(r0r, rhl); pr_debug("Right_r0 = 0x%llx\n", r0r); @@ -1150,17 +1150,11 @@ static int rt1305_i2c_probe(struct i2c_client *i2c, rt1305_reset(rt1305->regmap); rt1305_calibrate(rt1305); - return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt1305, + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1305, rt1305_dai, ARRAY_SIZE(rt1305_dai)); } -static int rt1305_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_component(&i2c->dev); - - return 0; -} - static void rt1305_i2c_shutdown(struct i2c_client *client) { struct rt1305_priv *rt1305 = i2c_get_clientdata(client); @@ -1180,7 +1174,6 @@ static struct i2c_driver rt1305_i2c_driver = { #endif }, .probe = rt1305_i2c_probe, - .remove = rt1305_i2c_remove, .shutdown = rt1305_i2c_shutdown, .id_table = rt1305_i2c_id, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 1570b91bf018..dca82dd6e3bf 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -64,8 +64,8 @@ static const struct reg_sequence rt5514_patch[] = { {RT5514_ANA_CTRL_LDO10, 0x00028604}, {RT5514_ANA_CTRL_ADCFED, 0x00000800}, {RT5514_ASRC_IN_CTRL1, 0x00000003}, - {RT5514_DOWNFILTER0_CTRL3, 0x10000362}, - {RT5514_DOWNFILTER1_CTRL3, 0x10000362}, + {RT5514_DOWNFILTER0_CTRL3, 0x10000352}, + {RT5514_DOWNFILTER1_CTRL3, 0x10000352}, }; static const struct reg_default rt5514_reg[] = { @@ -92,10 +92,10 @@ static const struct reg_default rt5514_reg[] = { {RT5514_ASRC_IN_CTRL1, 0x00000003}, {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f}, {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f}, - {RT5514_DOWNFILTER0_CTRL3, 0x10000362}, + {RT5514_DOWNFILTER0_CTRL3, 0x10000352}, {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f}, {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f}, - {RT5514_DOWNFILTER1_CTRL3, 0x10000362}, + {RT5514_DOWNFILTER1_CTRL3, 0x10000352}, {RT5514_ANA_CTRL_LDO10, 0x00028604}, {RT5514_ANA_CTRL_LDO18_16, 0x02000345}, {RT5514_ANA_CTRL_ADC12, 0x0000a2a8}, diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index cf6dce69eb2a..865f49ac38dd 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -105,9 +105,9 @@ static bool rt5631_volatile_register(struct device *dev, unsigned int reg) case RT5631_INDEX_ADD: case RT5631_INDEX_DATA: case RT5631_EQ_CTRL: - return 1; + return true; default: - return 0; + return false; } } @@ -164,9 +164,9 @@ static bool rt5631_readable_register(struct device *dev, unsigned int reg) case RT5631_VENDOR_ID: case RT5631_VENDOR_ID1: case RT5631_VENDOR_ID2: - return 1; + return true; default: - return 0; + return false; } } @@ -229,10 +229,10 @@ static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG, static const struct snd_kcontrol_new rt5631_snd_controls[] = { /* MIC */ SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum), - SOC_SINGLE_TLV("MIC1 Boost", RT5631_MIC_CTRL_2, + SOC_SINGLE_TLV("MIC1 Boost Volume", RT5631_MIC_CTRL_2, RT5631_MIC1_BOOST_SHIFT, 8, 0, mic_bst_tlv), SOC_ENUM("MIC2 Mode Control", rt5631_mic2_mode_enum), - SOC_SINGLE_TLV("MIC2 Boost", RT5631_MIC_CTRL_2, + SOC_SINGLE_TLV("MIC2 Boost Volume", RT5631_MIC_CTRL_2, RT5631_MIC2_BOOST_SHIFT, 8, 0, mic_bst_tlv), /* MONO IN */ SOC_ENUM("MONOIN Mode Control", rt5631_monoin_mode_enum), diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 8bf8d360c25f..27770143ae8f 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1665,6 +1665,7 @@ static int get_sdp_info(struct snd_soc_component *component, int dai_id) break; case RT5640_IF_113: ret |= RT5640_U_IF1; + /* fall through */ case RT5640_IF_312: case RT5640_IF_213: ret |= RT5640_U_IF2; @@ -1680,6 +1681,7 @@ static int get_sdp_info(struct snd_soc_component *component, int dai_id) break; case RT5640_IF_223: ret |= RT5640_U_IF1; + /* fall through */ case RT5640_IF_123: case RT5640_IF_321: ret |= RT5640_U_IF2; diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 6b5669f3e85d..985852fd9723 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -331,11 +331,13 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = { SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL, RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 175, 0, dac_vol_tlv), - /* IN1/IN2 Control */ + /* IN1/IN2/IN3 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2, RT5651_BST_SFT1, 8, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2, RT5651_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5651_IN3, + RT5651_BST_SFT1, 8, 0, bst_tlv), /* INL/INR Volume Control */ SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL, RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT, @@ -1581,6 +1583,24 @@ static void rt5651_disable_micbias1_for_ovcd(struct snd_soc_component *component snd_soc_dapm_mutex_unlock(dapm); } +static void rt5651_enable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_NOR); + rt5651->ovcd_irq_enabled = true; +} + +static void rt5651_disable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_BP); + rt5651->ovcd_irq_enabled = false; +} + static void rt5651_clear_micbias1_ovcd(struct snd_soc_component *component) { snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, @@ -1622,10 +1642,80 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component) return val == 0; } -/* Jack detect timings */ +/* Jack detect and button-press timings */ #define JACK_SETTLE_TIME 100 /* milli seconds */ #define JACK_DETECT_COUNT 5 #define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */ +#define JACK_UNPLUG_TIME 80 /* milli seconds */ +#define BP_POLL_TIME 10 /* milli seconds */ +#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */ +#define BP_THRESHOLD 3 + +static void rt5651_start_button_press_work(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + rt5651->poll_count = 0; + rt5651->press_count = 0; + rt5651->release_count = 0; + rt5651->pressed = false; + rt5651->press_reported = false; + rt5651_clear_micbias1_ovcd(component); + schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static void rt5651_button_press_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, bp_work.work); + struct snd_soc_component *component = rt5651->component; + + /* Check the jack was not removed underneath us */ + if (!rt5651_jack_inserted(component)) + return; + + if (rt5651_micbias1_ovcd(component)) { + rt5651->release_count = 0; + rt5651->press_count++; + /* Remember till after JACK_UNPLUG_TIME wait */ + if (rt5651->press_count >= BP_THRESHOLD) + rt5651->pressed = true; + rt5651_clear_micbias1_ovcd(component); + } else { + rt5651->press_count = 0; + rt5651->release_count++; + } + + /* + * The pins get temporarily shorted on jack unplug, so we poll for + * at least JACK_UNPLUG_TIME milli-seconds before reporting a press. + */ + rt5651->poll_count++; + if (rt5651->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) { + schedule_delayed_work(&rt5651->bp_work, + msecs_to_jiffies(BP_POLL_TIME)); + return; + } + + if (rt5651->pressed && !rt5651->press_reported) { + dev_dbg(component->dev, "headset button press\n"); + snd_soc_jack_report(rt5651->hp_jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + rt5651->press_reported = true; + } + + if (rt5651->release_count >= BP_THRESHOLD) { + if (rt5651->press_reported) { + dev_dbg(component->dev, "headset button release\n"); + snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); + } + /* Re-enable OVCD IRQ to detect next press */ + rt5651_enable_micbias1_ovcd_irq(component); + return; /* Stop polling */ + } + + schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} static int rt5651_detect_headset(struct snd_soc_component *component) { @@ -1676,15 +1766,58 @@ static void rt5651_jack_detect_work(struct work_struct *work) { struct rt5651_priv *rt5651 = container_of(work, struct rt5651_priv, jack_detect_work); + struct snd_soc_component *component = rt5651->component; int report = 0; - if (rt5651_jack_inserted(rt5651->component)) { - rt5651_enable_micbias1_for_ovcd(rt5651->component); - report = rt5651_detect_headset(rt5651->component); - rt5651_disable_micbias1_for_ovcd(rt5651->component); + if (!rt5651_jack_inserted(component)) { + /* Jack removed, or spurious IRQ? */ + if (rt5651->hp_jack->status & SND_JACK_HEADPHONE) { + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + cancel_delayed_work_sync(&rt5651->bp_work); + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_disable_micbias1_for_ovcd(component); + } + snd_soc_jack_report(rt5651->hp_jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(component->dev, "jack unplugged\n"); + } + } else if (!(rt5651->hp_jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted */ + WARN_ON(rt5651->ovcd_irq_enabled); + rt5651_enable_micbias1_for_ovcd(component); + report = rt5651_detect_headset(component); + if (report == SND_JACK_HEADSET) { + /* Enable ovcd IRQ for button press detect. */ + rt5651_enable_micbias1_ovcd_irq(component); + } else { + /* No more need for overcurrent detect. */ + rt5651_disable_micbias1_for_ovcd(component); + } + dev_dbg(component->dev, "detect report %#02x\n", report); + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); + } else if (rt5651->ovcd_irq_enabled && rt5651_micbias1_ovcd(component)) { + dev_dbg(component->dev, "OVCD IRQ\n"); + + /* + * The ovcd IRQ keeps firing while the button is pressed, so + * we disable it and start polling the button until released. + * + * The disable will make the IRQ pin 0 again and since we get + * IRQs on both edges (so as to detect both jack plugin and + * unplug) this means we will immediately get another IRQ. + * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP. + */ + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_start_button_press_work(component); + + /* + * If the jack-detect IRQ flag goes high (unplug) after our + * above rt5651_jack_inserted() check and before we have + * disabled the OVCD IRQ, the IRQ pin will stay high and as + * we react to edges, we miss the unplug event -> recheck. + */ + queue_work(system_long_wq, &rt5651->jack_detect_work); } - - snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); } static irqreturn_t rt5651_irq(int irq, void *data) @@ -1696,14 +1829,18 @@ static irqreturn_t rt5651_irq(int irq, void *data) return IRQ_HANDLED; } -static int rt5651_set_jack(struct snd_soc_component *component, - struct snd_soc_jack *hp_jack, void *data) +static void rt5651_cancel_work(void *data) { - struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); - int ret; + struct rt5651_priv *rt5651 = data; - if (!rt5651->irq) - return -EINVAL; + cancel_work_sync(&rt5651->jack_detect_work); + cancel_delayed_work_sync(&rt5651->bp_work); +} + +static void rt5651_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); /* IRQ output on GPIO1 */ snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1, @@ -1730,10 +1867,10 @@ static int rt5651_set_jack(struct snd_soc_component *component, RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN); break; case RT5651_JD_NULL: - return 0; + return; default: dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); - return -EINVAL; + return; } /* Enable jack detect power */ @@ -1767,19 +1904,39 @@ static int rt5651_set_jack(struct snd_soc_component *component, RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN); rt5651->hp_jack = hp_jack; - - ret = devm_request_threaded_irq(component->dev, rt5651->irq, NULL, - rt5651_irq, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, "rt5651", rt5651); - if (ret) { - dev_err(component->dev, "Failed to reguest IRQ: %d\n", ret); - return ret; + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + rt5651_enable_micbias1_for_ovcd(component); + rt5651_enable_micbias1_ovcd_irq(component); } + enable_irq(rt5651->irq); /* sync initial jack state */ queue_work(system_power_efficient_wq, &rt5651->jack_detect_work); +} + +static void rt5651_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + disable_irq(rt5651->irq); + rt5651_cancel_work(rt5651); + + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); + } + + rt5651->hp_jack = NULL; +} + +static int rt5651_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + rt5651_enable_jack_detect(component, jack); + else + rt5651_disable_jack_detect(component); return 0; } @@ -2034,8 +2191,26 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->irq = i2c->irq; rt5651->hp_mute = 1; + INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work); INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); + /* Make sure work is stopped on probe-error / remove */ + ret = devm_add_action_or_reset(&i2c->dev, rt5651_cancel_work, rt5651); + if (ret) + return ret; + + ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5651", rt5651); + if (ret == 0) { + /* Gets re-enabled by rt5651_set_jack() */ + disable_irq(rt5651->irq); + } else { + dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + rt5651->irq, ret); + rt5651->irq = -ENXIO; + } + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5651, rt5651_dai, ARRAY_SIZE(rt5651_dai)); @@ -2043,15 +2218,6 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, return ret; } -static int rt5651_i2c_remove(struct i2c_client *i2c) -{ - struct rt5651_priv *rt5651 = i2c_get_clientdata(i2c); - - cancel_work_sync(&rt5651->jack_detect_work); - - return 0; -} - static struct i2c_driver rt5651_i2c_driver = { .driver = { .name = "rt5651", @@ -2059,7 +2225,6 @@ static struct i2c_driver rt5651_i2c_driver = { .of_match_table = of_match_ptr(rt5651_of_match), }, .probe = rt5651_i2c_probe, - .remove = rt5651_i2c_remove, .id_table = rt5651_i2c_id, }; module_i2c_driver(rt5651_i2c_driver); diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h index 3a0968c53fde..ac6de6fb5414 100644 --- a/sound/soc/codecs/rt5651.h +++ b/sound/soc/codecs/rt5651.h @@ -2071,8 +2071,16 @@ struct rt5651_pll_code { struct rt5651_priv { struct snd_soc_component *component; struct regmap *regmap; + /* Jack and button detect data */ struct snd_soc_jack *hp_jack; struct work_struct jack_detect_work; + struct delayed_work bp_work; + bool ovcd_irq_enabled; + bool pressed; + bool press_reported; + int press_count; + int release_count; + int poll_count; unsigned int jd_src; unsigned int ovcd_th; unsigned int ovcd_sf; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 8a0181a2db08..9b7a1833d331 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4417,6 +4417,7 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, break; case 25: slot_width_25 = 0x8080; + /* fall through */ case 24: val |= (2 << 8); break; @@ -5007,7 +5008,7 @@ static const struct regmap_config rt5677_regmap = { }; static const struct of_device_id rt5677_of_match[] = { - { .compatible = "realtek,rt5677", RT5677 }, + { .compatible = "realtek,rt5677", .data = (const void *)RT5677 }, { } }; MODULE_DEVICE_TABLE(of, rt5677_of_match); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c new file mode 100644 index 000000000000..640d400ca013 --- /dev/null +++ b/sound/soc/codecs/rt5682.c @@ -0,0 +1,2681 @@ +/* + * rt5682.c -- RT5682 ALSA SoC audio component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/rt5682.h> + +#include "rl6231.h" +#include "rt5682.h" + +#define RT5682_NUM_SUPPLIES 3 + +static const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5682_priv { + struct snd_soc_component *component; + struct rt5682_platform_data pdata; + struct regmap *regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5682_AIFS]; + int bclk[RT5682_AIFS]; + int master[RT5682_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_sequence patch_list[] = { + {0x01c1, 0x1000}, +}; + +static const struct reg_default rt5682_reg[] = { + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x800f}, + {0x000b, 0x0000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0019, 0xafaf}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x0080}, + {0x0044, 0x0c0c}, + {0x0049, 0x0000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003f}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0a10}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008d, 0x0000}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x100c}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0009}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f1, 0x00d0}, + {0x00f2, 0x00d0}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6530}, + {0x0100, 0xa0a0}, + {0x010b, 0x0000}, + {0x010c, 0xae00}, + {0x010d, 0xaaa0}, + {0x010e, 0x8aa2}, + {0x010f, 0x02a2}, + {0x0110, 0xc000}, + {0x0111, 0x04a2}, + {0x0112, 0x2800}, + {0x0113, 0x0000}, + {0x0117, 0x0100}, + {0x0125, 0x0410}, + {0x0132, 0x6026}, + {0x0136, 0x5555}, + {0x0138, 0x3700}, + {0x013a, 0x2000}, + {0x013b, 0x2000}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0142, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0149, 0x0000}, + {0x0150, 0x79a1}, + {0x0151, 0x0000}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0021}, + {0x0190, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01af, 0x0000}, + {0x01b0, 0x0400}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0000}, + {0x01b4, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0008}, + {0x01d8, 0x0029}, + {0x01d9, 0x3333}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa005}, + {0x0212, 0x824c}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x0250, 0x4500}, + {0x0251, 0x40b3}, + {0x0252, 0x0000}, + {0x0253, 0x0000}, + {0x0254, 0x0000}, + {0x0255, 0x0000}, + {0x0256, 0x0000}, + {0x0257, 0x0000}, + {0x0258, 0x0000}, + {0x0259, 0x0000}, + {0x025a, 0x0005}, + {0x0270, 0x0000}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5682_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_CBJ_CTRL_2: + case RT5682_INT_ST_1: + case RT5682_4BTN_IL_CMD_1: + case RT5682_AJD1_CTRL: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_DEVICE_ID: + case RT5682_I2C_MODE: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_EFUSE_CTRL_2: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_EFUSE_CTRL_6...RT5682_EFUSE_CTRL_11: + case RT5682_HP_CALIB_STA_1...RT5682_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5682_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_VERSION_ID: + case RT5682_VENDOR_ID: + case RT5682_DEVICE_ID: + case RT5682_HP_CTRL_1: + case RT5682_HP_CTRL_2: + case RT5682_HPL_GAIN: + case RT5682_HPR_GAIN: + case RT5682_I2C_CTRL: + case RT5682_CBJ_BST_CTRL: + case RT5682_CBJ_CTRL_1: + case RT5682_CBJ_CTRL_2: + case RT5682_CBJ_CTRL_3: + case RT5682_CBJ_CTRL_4: + case RT5682_CBJ_CTRL_5: + case RT5682_CBJ_CTRL_6: + case RT5682_CBJ_CTRL_7: + case RT5682_DAC1_DIG_VOL: + case RT5682_STO1_ADC_DIG_VOL: + case RT5682_STO1_ADC_BOOST: + case RT5682_HP_IMP_GAIN_1: + case RT5682_HP_IMP_GAIN_2: + case RT5682_SIDETONE_CTRL: + case RT5682_STO1_ADC_MIXER: + case RT5682_AD_DA_MIXER: + case RT5682_STO1_DAC_MIXER: + case RT5682_A_DAC1_MUX: + case RT5682_DIG_INF2_DATA: + case RT5682_REC_MIXER: + case RT5682_CAL_REC: + case RT5682_ALC_BACK_GAIN: + case RT5682_PWR_DIG_1: + case RT5682_PWR_DIG_2: + case RT5682_PWR_ANLG_1: + case RT5682_PWR_ANLG_2: + case RT5682_PWR_ANLG_3: + case RT5682_PWR_MIXER: + case RT5682_PWR_VOL: + case RT5682_CLK_DET: + case RT5682_RESET_LPF_CTRL: + case RT5682_RESET_HPF_CTRL: + case RT5682_DMIC_CTRL_1: + case RT5682_I2S1_SDP: + case RT5682_I2S2_SDP: + case RT5682_ADDA_CLK_1: + case RT5682_ADDA_CLK_2: + case RT5682_I2S1_F_DIV_CTRL_1: + case RT5682_I2S1_F_DIV_CTRL_2: + case RT5682_TDM_CTRL: + case RT5682_TDM_ADDA_CTRL_1: + case RT5682_TDM_ADDA_CTRL_2: + case RT5682_DATA_SEL_CTRL_1: + case RT5682_TDM_TCON_CTRL: + case RT5682_GLB_CLK: + case RT5682_PLL_CTRL_1: + case RT5682_PLL_CTRL_2: + case RT5682_PLL_TRACK_1: + case RT5682_PLL_TRACK_2: + case RT5682_PLL_TRACK_3: + case RT5682_PLL_TRACK_4: + case RT5682_PLL_TRACK_5: + case RT5682_PLL_TRACK_6: + case RT5682_PLL_TRACK_11: + case RT5682_SDW_REF_CLK: + case RT5682_DEPOP_1: + case RT5682_DEPOP_2: + case RT5682_HP_CHARGE_PUMP_1: + case RT5682_HP_CHARGE_PUMP_2: + case RT5682_MICBIAS_1: + case RT5682_MICBIAS_2: + case RT5682_PLL_TRACK_12: + case RT5682_PLL_TRACK_14: + case RT5682_PLL2_CTRL_1: + case RT5682_PLL2_CTRL_2: + case RT5682_PLL2_CTRL_3: + case RT5682_PLL2_CTRL_4: + case RT5682_RC_CLK_CTRL: + case RT5682_I2S_M_CLK_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_2: + case RT5682_EQ_CTRL_1: + case RT5682_EQ_CTRL_2: + case RT5682_IRQ_CTRL_1: + case RT5682_IRQ_CTRL_2: + case RT5682_IRQ_CTRL_3: + case RT5682_IRQ_CTRL_4: + case RT5682_INT_ST_1: + case RT5682_GPIO_CTRL_1: + case RT5682_GPIO_CTRL_2: + case RT5682_GPIO_CTRL_3: + case RT5682_HP_AMP_DET_CTRL_1: + case RT5682_HP_AMP_DET_CTRL_2: + case RT5682_MID_HP_AMP_DET: + case RT5682_LOW_HP_AMP_DET: + case RT5682_DELAY_BUF_CTRL: + case RT5682_SV_ZCD_1: + case RT5682_SV_ZCD_2: + case RT5682_IL_CMD_1: + case RT5682_IL_CMD_2: + case RT5682_IL_CMD_3: + case RT5682_IL_CMD_4: + case RT5682_IL_CMD_5: + case RT5682_IL_CMD_6: + case RT5682_4BTN_IL_CMD_1: + case RT5682_4BTN_IL_CMD_2: + case RT5682_4BTN_IL_CMD_3: + case RT5682_4BTN_IL_CMD_4: + case RT5682_4BTN_IL_CMD_5: + case RT5682_4BTN_IL_CMD_6: + case RT5682_4BTN_IL_CMD_7: + case RT5682_ADC_STO1_HP_CTRL_1: + case RT5682_ADC_STO1_HP_CTRL_2: + case RT5682_AJD1_CTRL: + case RT5682_JD1_THD: + case RT5682_JD2_THD: + case RT5682_JD_CTRL_1: + case RT5682_DUMMY_1: + case RT5682_DUMMY_2: + case RT5682_DUMMY_3: + case RT5682_DAC_ADC_DIG_VOL1: + case RT5682_BIAS_CUR_CTRL_2: + case RT5682_BIAS_CUR_CTRL_3: + case RT5682_BIAS_CUR_CTRL_4: + case RT5682_BIAS_CUR_CTRL_5: + case RT5682_BIAS_CUR_CTRL_6: + case RT5682_BIAS_CUR_CTRL_7: + case RT5682_BIAS_CUR_CTRL_8: + case RT5682_BIAS_CUR_CTRL_9: + case RT5682_BIAS_CUR_CTRL_10: + case RT5682_VREF_REC_OP_FB_CAP_CTRL: + case RT5682_CHARGE_PUMP_1: + case RT5682_DIG_IN_CTRL_1: + case RT5682_PAD_DRIVING_CTRL: + case RT5682_SOFT_RAMP_DEPOP: + case RT5682_CHOP_DAC: + case RT5682_CHOP_ADC: + case RT5682_CALIB_ADC_CTRL: + case RT5682_VOL_TEST: + case RT5682_SPKVDD_DET_STA: + case RT5682_TEST_MODE_CTRL_1: + case RT5682_TEST_MODE_CTRL_2: + case RT5682_TEST_MODE_CTRL_3: + case RT5682_TEST_MODE_CTRL_4: + case RT5682_TEST_MODE_CTRL_5: + case RT5682_PLL1_INTERNAL: + case RT5682_PLL2_INTERNAL: + case RT5682_STO_NG2_CTRL_1: + case RT5682_STO_NG2_CTRL_2: + case RT5682_STO_NG2_CTRL_3: + case RT5682_STO_NG2_CTRL_4: + case RT5682_STO_NG2_CTRL_5: + case RT5682_STO_NG2_CTRL_6: + case RT5682_STO_NG2_CTRL_7: + case RT5682_STO_NG2_CTRL_8: + case RT5682_STO_NG2_CTRL_9: + case RT5682_STO_NG2_CTRL_10: + case RT5682_STO1_DAC_SIL_DET: + case RT5682_SIL_PSV_CTRL1: + case RT5682_SIL_PSV_CTRL2: + case RT5682_SIL_PSV_CTRL3: + case RT5682_SIL_PSV_CTRL4: + case RT5682_SIL_PSV_CTRL5: + case RT5682_HP_IMP_SENS_CTRL_01: + case RT5682_HP_IMP_SENS_CTRL_02: + case RT5682_HP_IMP_SENS_CTRL_03: + case RT5682_HP_IMP_SENS_CTRL_04: + case RT5682_HP_IMP_SENS_CTRL_05: + case RT5682_HP_IMP_SENS_CTRL_06: + case RT5682_HP_IMP_SENS_CTRL_07: + case RT5682_HP_IMP_SENS_CTRL_08: + case RT5682_HP_IMP_SENS_CTRL_09: + case RT5682_HP_IMP_SENS_CTRL_10: + case RT5682_HP_IMP_SENS_CTRL_11: + case RT5682_HP_IMP_SENS_CTRL_12: + case RT5682_HP_IMP_SENS_CTRL_13: + case RT5682_HP_IMP_SENS_CTRL_14: + case RT5682_HP_IMP_SENS_CTRL_15: + case RT5682_HP_IMP_SENS_CTRL_16: + case RT5682_HP_IMP_SENS_CTRL_17: + case RT5682_HP_IMP_SENS_CTRL_18: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_HP_IMP_SENS_CTRL_20: + case RT5682_HP_IMP_SENS_CTRL_21: + case RT5682_HP_IMP_SENS_CTRL_22: + case RT5682_HP_IMP_SENS_CTRL_23: + case RT5682_HP_IMP_SENS_CTRL_24: + case RT5682_HP_IMP_SENS_CTRL_25: + case RT5682_HP_IMP_SENS_CTRL_26: + case RT5682_HP_IMP_SENS_CTRL_27: + case RT5682_HP_IMP_SENS_CTRL_28: + case RT5682_HP_IMP_SENS_CTRL_29: + case RT5682_HP_IMP_SENS_CTRL_30: + case RT5682_HP_IMP_SENS_CTRL_31: + case RT5682_HP_IMP_SENS_CTRL_32: + case RT5682_HP_IMP_SENS_CTRL_33: + case RT5682_HP_IMP_SENS_CTRL_34: + case RT5682_HP_IMP_SENS_CTRL_35: + case RT5682_HP_IMP_SENS_CTRL_36: + case RT5682_HP_IMP_SENS_CTRL_37: + case RT5682_HP_IMP_SENS_CTRL_38: + case RT5682_HP_IMP_SENS_CTRL_39: + case RT5682_HP_IMP_SENS_CTRL_40: + case RT5682_HP_IMP_SENS_CTRL_41: + case RT5682_HP_IMP_SENS_CTRL_42: + case RT5682_HP_IMP_SENS_CTRL_43: + case RT5682_HP_LOGIC_CTRL_1: + case RT5682_HP_LOGIC_CTRL_2: + case RT5682_HP_LOGIC_CTRL_3: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_HP_CALIB_CTRL_2: + case RT5682_HP_CALIB_CTRL_3: + case RT5682_HP_CALIB_CTRL_4: + case RT5682_HP_CALIB_CTRL_5: + case RT5682_HP_CALIB_CTRL_6: + case RT5682_HP_CALIB_CTRL_7: + case RT5682_HP_CALIB_CTRL_9: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_HP_CALIB_CTRL_11: + case RT5682_HP_CALIB_STA_1: + case RT5682_HP_CALIB_STA_2: + case RT5682_HP_CALIB_STA_3: + case RT5682_HP_CALIB_STA_4: + case RT5682_HP_CALIB_STA_5: + case RT5682_HP_CALIB_STA_6: + case RT5682_HP_CALIB_STA_7: + case RT5682_HP_CALIB_STA_8: + case RT5682_HP_CALIB_STA_9: + case RT5682_HP_CALIB_STA_10: + case RT5682_HP_CALIB_STA_11: + case RT5682_SAR_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_3: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_5: + case RT5682_SAR_IL_CMD_6: + case RT5682_SAR_IL_CMD_7: + case RT5682_SAR_IL_CMD_8: + case RT5682_SAR_IL_CMD_9: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_SAR_IL_CMD_12: + case RT5682_SAR_IL_CMD_13: + case RT5682_EFUSE_CTRL_1: + case RT5682_EFUSE_CTRL_2: + case RT5682_EFUSE_CTRL_3: + case RT5682_EFUSE_CTRL_4: + case RT5682_EFUSE_CTRL_5: + case RT5682_EFUSE_CTRL_6: + case RT5682_EFUSE_CTRL_7: + case RT5682_EFUSE_CTRL_8: + case RT5682_EFUSE_CTRL_9: + case RT5682_EFUSE_CTRL_10: + case RT5682_EFUSE_CTRL_11: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_DRC1_CTRL_0: + case RT5682_DRC1_CTRL_1: + case RT5682_DRC1_CTRL_2: + case RT5682_DRC1_CTRL_3: + case RT5682_DRC1_CTRL_4: + case RT5682_DRC1_CTRL_5: + case RT5682_DRC1_CTRL_6: + case RT5682_DRC1_HARD_LMT_CTRL_1: + case RT5682_DRC1_HARD_LMT_CTRL_2: + case RT5682_DRC1_PRIV_1: + case RT5682_DRC1_PRIV_2: + case RT5682_DRC1_PRIV_3: + case RT5682_DRC1_PRIV_4: + case RT5682_DRC1_PRIV_5: + case RT5682_DRC1_PRIV_6: + case RT5682_DRC1_PRIV_7: + case RT5682_DRC1_PRIV_8: + case RT5682_EQ_AUTO_RCV_CTRL1: + case RT5682_EQ_AUTO_RCV_CTRL2: + case RT5682_EQ_AUTO_RCV_CTRL3: + case RT5682_EQ_AUTO_RCV_CTRL4: + case RT5682_EQ_AUTO_RCV_CTRL5: + case RT5682_EQ_AUTO_RCV_CTRL6: + case RT5682_EQ_AUTO_RCV_CTRL7: + case RT5682_EQ_AUTO_RCV_CTRL8: + case RT5682_EQ_AUTO_RCV_CTRL9: + case RT5682_EQ_AUTO_RCV_CTRL10: + case RT5682_EQ_AUTO_RCV_CTRL11: + case RT5682_EQ_AUTO_RCV_CTRL12: + case RT5682_EQ_AUTO_RCV_CTRL13: + case RT5682_ADC_L_EQ_LPF1_A1: + case RT5682_R_EQ_LPF1_A1: + case RT5682_L_EQ_LPF1_H0: + case RT5682_R_EQ_LPF1_H0: + case RT5682_L_EQ_BPF1_A1: + case RT5682_R_EQ_BPF1_A1: + case RT5682_L_EQ_BPF1_A2: + case RT5682_R_EQ_BPF1_A2: + case RT5682_L_EQ_BPF1_H0: + case RT5682_R_EQ_BPF1_H0: + case RT5682_L_EQ_BPF2_A1: + case RT5682_R_EQ_BPF2_A1: + case RT5682_L_EQ_BPF2_A2: + case RT5682_R_EQ_BPF2_A2: + case RT5682_L_EQ_BPF2_H0: + case RT5682_R_EQ_BPF2_H0: + case RT5682_L_EQ_BPF3_A1: + case RT5682_R_EQ_BPF3_A1: + case RT5682_L_EQ_BPF3_A2: + case RT5682_R_EQ_BPF3_A2: + case RT5682_L_EQ_BPF3_H0: + case RT5682_R_EQ_BPF3_H0: + case RT5682_L_EQ_BPF4_A1: + case RT5682_R_EQ_BPF4_A1: + case RT5682_L_EQ_BPF4_A2: + case RT5682_R_EQ_BPF4_A2: + case RT5682_L_EQ_BPF4_H0: + case RT5682_R_EQ_BPF4_H0: + case RT5682_L_EQ_HPF1_A1: + case RT5682_R_EQ_HPF1_A1: + case RT5682_L_EQ_HPF1_H0: + case RT5682_R_EQ_HPF1_H0: + case RT5682_L_EQ_PRE_VOL: + case RT5682_R_EQ_PRE_VOL: + case RT5682_L_EQ_POST_VOL: + case RT5682_R_EQ_POST_VOL: + case RT5682_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5682_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682_if2_adc_enum, + RT5682_DIG_INF2_DATA, RT5682_IF2_ADC_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_01_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC1_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_23_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC2_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_45_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC3_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_67_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC4_SEL_SFT, rt5682_data_select); + +static const struct snd_kcontrol_new rt5682_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682_if2_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); + +static void rt5682_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT5682_RESET, 0); + regmap_write(regmap, RT5682_I2C_MODE, 1); +} +/** + * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + + switch (clk_src) { + case RT5682_CLK_SEL_SYS: + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5682_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_2, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5682_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_3, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_sel_asrc_clk_src); + +static int rt5682_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read32(component, RT5682_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val); + pr_debug("%s btn_type=%x\n", __func__, btn_type); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_2, 0x10, 0x10); + + return btn_type; +} + +static void rt5682_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_BTN); + snd_soc_component_write(component, RT5682_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK, + RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK, RT5682_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_RST); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_TYPE); + } +} + +/** + * rt5682_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5682_headset_detect(struct snd_soc_component *component, + int jack_insert) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val, count; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH); + + count = 0; + val = snd_soc_component_read32(component, RT5682_CBJ_CTRL_2) + & RT5682_JACK_TYPE_MASK; + while (val == 0 && count < 50) { + usleep_range(10000, 15000); + val = snd_soc_component_read32(component, + RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; + count++; + } + + switch (val) { + case 0x1: + case 0x2: + rt5682->jack_type = SND_JACK_HEADSET; + rt5682_enable_push_button_irq(component, true); + break; + default: + rt5682->jack_type = SND_JACK_HEADPHONE; + } + + } else { + rt5682_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + + rt5682->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type); + return rt5682->jack_type; +} + +static irqreturn_t rt5682_irq(int irq, void *data) +{ + struct rt5682_priv *rt5682 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5682_jd_check_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv, + jd_check_work.work); + + if (snd_soc_component_read32(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK) { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5682->jd_check_work, 500); + } +} + +static int rt5682_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (rt5682->pdata.jd_src) { + case RT5682_JD1: + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5682_CBJ_CTRL_1, 0xd042); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_3, + RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH | RT5682_PWR_JDL, + RT5682_PWR_JDH | RT5682_PWR_JDL); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, + RT5682_JD1_EN | RT5682_JD1_POL_NOR); + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + break; + + case RT5682_JD_NULL: + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + rt5682->hs_jack = hs_jack; + + return 0; +} + +static void rt5682_jack_detect_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = + container_of(work, struct rt5682_priv, jack_detect_work.work); + int val, btn_type; + + while (!rt5682->component) + usleep_range(10000, 15000); + + while (!rt5682->component->card->instantiated) + usleep_range(10000, 15000); + + mutex_lock(&rt5682->calibrate_mutex); + + val = snd_soc_component_read32(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5682->jack_type == 0) { + /* jack was out, report jack type */ + rt5682->jack_type = + rt5682_headset_detect(rt5682->component, 1); + } else { + /* jack is already in, report button event */ + rt5682->jack_type = SND_JACK_HEADSET; + btn_type = rt5682_button_detect(rt5682->component); + /** + * rt5682 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5682->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5682->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5682->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5682->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5682->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + } + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682->jd_check_work); + + mutex_unlock(&rt5682->calibrate_mutex); +} + +static const struct snd_kcontrol_new rt5682_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5682_HPL_GAIN, + RT5682_HPR_GAIN, RT5682_G_HP_SFT, 15, 1, hp_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, + RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 175, 0, dac_vol_tlv), + + /* IN Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL, + RT5682_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_VOL_SFT, RT5682_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST, + RT5682_STO1_ADC_L_BST_SFT, RT5682_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + + +static int rt5682_div_sel(struct rt5682_priv *rt5682, + int target, const int div[], int size) +{ + int i; + + if (rt5682->sysclk < target) { + pr_err("sysclk rate %d is too low\n", + rt5682->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + pr_info("div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5682->sysclk) + return i; + if (target * div[i + 1] > rt5682->sysclk) { + pr_err("can't find div for sysclk %d\n", + rt5682->sysclk); + return i; + } + } + + if (target * div[i] < rt5682->sysclk) + pr_err("sysclk rate %d is too high\n", + rt5682->sysclk); + + return size - 1; + +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int idx = -EINVAL; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + idx = rt5682_div_sel(rt5682, 1500000, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1, + RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT); + + return 0; +} + +static int set_filter_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int ref, val, reg, sft, mask, idx = -EINVAL; + static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + + val = snd_soc_component_read32(component, RT5682_GPIO_CTRL_1) & + RT5682_GP4_PIN_MASK; + if (w->shift == RT5682_PWR_ADC_S1F_BIT && + val == RT5682_GP4_PIN_ADCDAT2) + ref = 256 * rt5682->lrck[RT5682_AIF2]; + else + ref = 256 * rt5682->lrck[RT5682_AIF1]; + + idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f)); + + if (w->shift == RT5682_PWR_ADC_S1F_BIT) { + reg = RT5682_PLL_TRACK_3; + sft = RT5682_ADC_OSR_SFT; + mask = RT5682_ADC_OSR_MASK; + } else { + reg = RT5682_PLL_TRACK_2; + sft = RT5682_DAC_OSR_SFT; + mask = RT5682_DAC_OSR_MASK; + } + + snd_soc_component_update_bits(component, reg, + RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT); + + /* select over sample rate */ + for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) { + if (rt5682->sysclk <= 12288000 * div_o[idx]) + break; + } + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + mask, idx << sft); + + return 0; +} + +static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read32(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5682_ADC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_3; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + case RT5682_DAC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_2; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read32(component, reg) >> shift) & 0xf; + switch (val) { + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5682_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5682_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5682_REC_MIXER, + RT5682_M_CBJ_RM1_L_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5682_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1L_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1R_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5682_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcl_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCL_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcr_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCR_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5682_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2L_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2R_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5682_if1_adc_slot_values[] = { + 0, + 2, + 4, + 6, +}; + +static const char * const rt5682_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_if1_adc_slot_enum, + RT5682_TDM_CTRL, RT5682_TDM_ADC_LCA_SFT, RT5682_TDM_ADC_LCA_MASK, + rt5682_if1_adc_slot_src, rt5682_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5682_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5682_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_l1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACL1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_r1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACR1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682_alg_dac_r1_enum); + +/* Out Switch */ +static const struct snd_kcontrol_new hpol_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_L_MUTE_SFT, 1, 1); +static const struct snd_kcontrol_new hpor_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_R_MUTE_SFT, 1, 1); + +static int rt5682_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, + RT5682_HP_LOGIC_CTRL_2, 0x0012); + snd_soc_component_write(component, + RT5682_HP_CTRL_2, 0x6000); + snd_soc_component_update_bits(component, RT5682_STO_NG2_CTRL_1, + RT5682_NG2_EN_MASK, RT5682_NG2_EN); + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x60); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x0); + snd_soc_component_write(component, + RT5682_HP_CTRL_2, 0x0000); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5655_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, 0); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, + RT5682_PWR_FV1); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, + RT5682_PWR_FV2); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + +static const unsigned int rt5682_adcdat_pin_values[] = { + 1, + 3, +}; + +static const char * const rt5682_adcdat_pin_select[] = { + "ADCDAT1", + "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum, + RT5682_GPIO_CTRL_1, RT5682_GP4_PIN_SFT, RT5682_GP4_PIN_MASK, + rt5682_adcdat_pin_select, rt5682_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum); + +static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5682_PWR_ANLG_3, RT5682_PWR_PLL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5682_PWR_ANLG_1, RT5682_PWR_VREF2_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682_PWR_ANLG_2, RT5682_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682_PWR_ANLG_2, RT5682_PWR_MB2_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix, + ARRAY_SIZE(rt5682_rec1_l_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682_PWR_ANLG_2, + RT5682_PWR_RM1_L_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682_CHOP_ADC, + RT5682_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_ADC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, 1, rt5682_sto1_adc_l_mix, + ARRAY_SIZE(rt5682_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL, + RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix, + ARRAY_SIZE(rt5682_sto1_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("BTN Detection Mode", RT5682_SAR_IL_CMD_1, + 14, 1, NULL, 0), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5682_PWR_DIG_1, RT5682_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5682_PWR_DIG_1, RT5682_PWR_I2S2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, + &rt5682_adcdat_pin_ctrl), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5682_dac_l_mix, ARRAY_SIZE(rt5682_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5682_dac_r_mix, ARRAY_SIZE(rt5682_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_DAC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_l_mix, ARRAY_SIZE(rt5682_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_r_mix, ARRAY_SIZE(rt5682_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5682_CHOP_DAC, + RT5682_CKGEN_DAC1_SFT, 0, NULL, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("HP Amp L", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1, + RT5682_PUMP_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1, + RT5682_CAPLESS_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0, + &hpol_switch), + SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0, + &hpor_switch), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET, + RT5682_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682_CLK_DET, + RT5682_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5682_CLK_DET, + RT5682_PLL2_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5682_CLK_DET, + RT5682_POW_CLK_DET_SFT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + +}; + +static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"ADC STO1 ASRC", NULL, "CLKDET"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + {"DAC STO1 ASRC", NULL, "CLKDET"}, + + /*Vref*/ + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"ADC Stereo1 Filter", NULL, "BTN Detection Mode"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"IF1_ADC Mux", NULL, "I2S1"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "HP Amp L"}, + {"HP Amp", NULL, "HP Amp R"}, + {"HP Amp", NULL, "Capless"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPOL Playback", "Switch", "HP Amp"}, + {"HPOR Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPOL Playback"}, + {"HPOR", NULL, "HPOR Playback"}, +}; + +static int rt5682_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int cl, val = 0; + + if (tx_mask || rx_mask) + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, RT5682_TDM_EN); + else + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, 0); + + switch (slots) { + case 4: + val |= RT5682_TDM_TX_CH_4; + val |= RT5682_TDM_RX_CH_4; + break; + case 6: + val |= RT5682_TDM_TX_CH_6; + val |= RT5682_TDM_RX_CH_6; + break; + case 8: + val |= RT5682_TDM_TX_CH_8; + val |= RT5682_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_CTRL, + RT5682_TDM_TX_CH_MASK | RT5682_TDM_RX_CH_MASK, val); + + switch (slot_width) { + case 8: + if (tx_mask || rx_mask) + return -EINVAL; + cl = RT5682_I2S1_TX_CHL_8 | RT5682_I2S1_RX_CHL_8; + break; + case 16: + val = RT5682_TDM_CL_16; + cl = RT5682_I2S1_TX_CHL_16 | RT5682_I2S1_RX_CHL_16; + break; + case 20: + val = RT5682_TDM_CL_20; + cl = RT5682_I2S1_TX_CHL_20 | RT5682_I2S1_RX_CHL_20; + break; + case 24: + val = RT5682_TDM_CL_24; + cl = RT5682_I2S1_TX_CHL_24 | RT5682_I2S1_RX_CHL_24; + break; + case 32: + val = RT5682_TDM_CL_32; + cl = RT5682_I2S1_TX_CHL_32 | RT5682_I2S1_RX_CHL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_CL_MASK, val); + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_TX_CHL_MASK | RT5682_I2S1_RX_CHL_MASK, cl); + + return 0; +} + + +static int rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int pre_div, frame_size; + + rt5682->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5682->sysclk, rt5682->lrck[dai->id]); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5682->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5682_I2S1_DL_20; + len_2 |= RT5682_I2S2_DL_20; + break; + case 24: + len_1 |= RT5682_I2S1_DL_24; + len_2 |= RT5682_I2S2_DL_24; + break; + case 32: + len_1 |= RT5682_I2S1_DL_32; + len_2 |= RT5682_I2S2_DL_24; + break; + case 8: + len_1 |= RT5682_I2S2_DL_8; + len_2 |= RT5682_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_DL_MASK, len_1); + if (rt5682->master[RT5682_AIF1]) { + snd_soc_component_update_bits(component, + RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK, + pre_div << RT5682_I2S_M_DIV_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_DIS); + break; + case RT5682_AIF2: + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_DL_MASK, len_2); + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_M_PD_MASK, + pre_div << RT5682_I2S2_M_PD_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5682->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5682->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5682_I2S_BP_INV; + tdm_ctrl |= RT5682_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_LP_INV | RT5682_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_BP_INV | RT5682_TDM_S_LP_INV | + RT5682_TDM_M_BP_INV | RT5682_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5682_I2S_DF_LEFT; + tdm_ctrl |= RT5682_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5682_I2S_DF_PCM_A; + tdm_ctrl |= RT5682_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5682_I2S_DF_PCM_B; + tdm_ctrl |= RT5682_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_MS_MASK | RT5682_TDM_S_BP_MASK | + RT5682_TDM_DF_MASK | RT5682_TDM_M_BP_MASK | + RT5682_TDM_M_LP_MASK | RT5682_TDM_S_LP_MASK, + tdm_ctrl | rt5682->master[dai->id]); + break; + case RT5682_AIF2: + if (rt5682->master[dai->id] == 0) + reg_val |= RT5682_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_MS_MASK | RT5682_I2S_BP_MASK | + RT5682_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5682_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5682->sysclk && clk_id == rt5682->sysclk_src) + return 0; + + switch (clk_id) { + case RT5682_SCLK_S_MCLK: + reg_val |= RT5682_SCLK_SRC_MCLK; + src = RT5682_CLK_SRC_MCLK; + break; + case RT5682_SCLK_S_PLL1: + reg_val |= RT5682_SCLK_SRC_PLL1; + src = RT5682_CLK_SRC_PLL1; + break; + case RT5682_SCLK_S_PLL2: + reg_val |= RT5682_SCLK_SRC_PLL2; + src = RT5682_CLK_SRC_PLL2; + break; + case RT5682_SCLK_S_RCCLK: + reg_val |= RT5682_SCLK_SRC_RCCLK; + src = RT5682_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, reg_val); + + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_SRC_MASK, + src << RT5682_I2S2_SRC_SFT); + } + + rt5682->sysclk = freq; + rt5682->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5682_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5682->pll_src && freq_in == rt5682->pll_in && + freq_out == rt5682->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5682->pll_in = 0; + rt5682->pll_out = 0; + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5682_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_MCLK); + break; + case RT5682_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_PLL1_SRC_MASK, RT5682_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5682_PLL_CTRL_1, + pll_code.n_code << RT5682_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5682_PLL_CTRL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT | + pll_code.m_bp << RT5682_PLL_M_BP_SFT | RT5682_PLL_RST); + + rt5682->pll_in = freq_in; + rt5682->pll_out = freq_out; + rt5682->pll_src = source; + + return 0; +} + +static int rt5682_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB | RT5682_PWR_BG, + RT5682_PWR_MB | RT5682_PWR_BG); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, RT5682_PWR_MB); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_MB | RT5682_PWR_BG, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5682_probe(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->component = component; + + return 0; +} + +static void rt5682_remove(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682_reset(rt5682->regmap); +} + +#ifdef CONFIG_PM +static int rt5682_suspend(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + return 0; +} + +static int rt5682_resume(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + return 0; +} +#else +#define rt5682_suspend NULL +#define rt5682_resume NULL +#endif + +#define RT5682_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5682_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5682_aif1_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_tdm_slot = rt5682_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops rt5682_aif2_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_bclk_ratio = rt5682_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5682_dai[] = { + { + .name = "rt5682-aif1", + .id = RT5682_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif1_dai_ops, + }, + { + .name = "rt5682-aif2", + .id = RT5682_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif2_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5682 = { + .probe = rt5682_probe, + .remove = rt5682_remove, + .suspend = rt5682_suspend, + .resume = rt5682_resume, + .set_bias_level = rt5682_set_bias_level, + .controls = rt5682_snd_controls, + .num_controls = ARRAY_SIZE(rt5682_snd_controls), + .dapm_widgets = rt5682_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5682_dapm_widgets), + .dapm_routes = rt5682_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5682_dapm_routes), + .set_sysclk = rt5682_set_component_sysclk, + .set_pll = rt5682_set_component_pll, + .set_jack = rt5682_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config rt5682_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = ARRAY_SIZE(rt5682_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5682_i2c_id[] = { + {"rt5682", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id); + +static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) +{ + + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5682->pdata.dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic1-clk-pin", + &rt5682->pdata.dmic1_clk_pin); + device_property_read_u32(dev, "realtek,jd-src", + &rt5682->pdata.jd_src); + + rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, + "realtek,ldo1-en-gpios", 0); + + return 0; +} + +static void rt5682_calibrate(struct rt5682_priv *rt5682) +{ + int value, count; + + mutex_lock(&rt5682->calibrate_mutex); + + rt5682_reset(rt5682->regmap); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf); + usleep_range(15000, 20000); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8001); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_write(rt5682->regmap, RT5682_STO1_DAC_MIXER, 0x2080); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0069); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); + regmap_write(rt5682->regmap, RT5682_HP_CTRL_2, 0x6000); + regmap_write(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, 0x0f26); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7f05); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_9, 0x000f); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8d01); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); + regmap_write(rt5682->regmap, RT5682_RESET_HPF_CTRL, 0x0000); + regmap_write(rt5682->regmap, RT5682_ADC_STO1_HP_CTRL_1, 0x3320); + + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5682->regmap, RT5682_HP_CALIB_STA_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + pr_err("HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); + + mutex_unlock(&rt5682->calibrate_mutex); + +} + +static int rt5682_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5682_priv *rt5682; + int i, ret; + unsigned int val; + + rt5682 = devm_kzalloc(&i2c->dev, sizeof(struct rt5682_priv), + GFP_KERNEL); + + if (rt5682 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5682); + + if (pdata) + rt5682->pdata = *pdata; + else + rt5682_parse_dt(rt5682, &i2c->dev); + + rt5682->regmap = devm_regmap_init_i2c(i2c, &rt5682_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5682->supplies); i++) + rt5682->supplies[i].supply = rt5682_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + if (gpio_is_valid(rt5682->pdata.ldo1_en)) { + if (devm_gpio_request_one(&i2c->dev, rt5682->pdata.ldo1_en, + GPIOF_OUT_INIT_HIGH, "rt5682")) + dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1); + usleep_range(10000, 15000); + + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5682\n", val); + return -ENODEV; + } + + rt5682_reset(rt5682->regmap); + + rt5682_calibrate(rt5682); + + ret = regmap_register_patch(rt5682->regmap, patch_list, + ARRAY_SIZE(patch_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + /* DMIC pin*/ + if (rt5682->pdata.dmic1_data_pin != RT5682_DMIC1_NULL) { + switch (rt5682->pdata.dmic1_data_pin) { + case RT5682_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP2_PIN_MASK, RT5682_GP2_PIN_DMIC_SDA); + break; + + case RT5682_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP5_PIN_MASK, RT5682_GP5_PIN_DMIC_SDA); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + switch (rt5682->pdata.dmic1_clk_pin) { + case RT5682_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_DMIC_CLK); + break; + + case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + } + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK, + RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + INIT_DELAYED_WORK(&rt5682->jd_check_work, + rt5682_jd_check_handler); + + mutex_init(&rt5682->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5682", rt5682); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5682, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); +} + +static void rt5682_i2c_shutdown(struct i2c_client *client) +{ + struct rt5682_priv *rt5682 = i2c_get_clientdata(client); + + rt5682_reset(rt5682->regmap); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5682_of_match[] = { + {.compatible = "realtek,rt5682i"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5682_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5682_acpi_match[] = { + {"10EC5682", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match); +#endif + +static struct i2c_driver rt5682_i2c_driver = { + .driver = { + .name = "rt5682", + .of_match_table = of_match_ptr(rt5682_of_match), + .acpi_match_table = ACPI_PTR(rt5682_acpi_match), + }, + .probe = rt5682_i2c_probe, + .shutdown = rt5682_i2c_shutdown, + .id_table = rt5682_i2c_id, +}; +module_i2c_driver(rt5682_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver"); +MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h new file mode 100644 index 000000000000..8068140ebe3f --- /dev/null +++ b/sound/soc/codecs/rt5682.h @@ -0,0 +1,1324 @@ +/* + * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver + * + * Copyright 2018 Realtek Microelectronics + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT5682_H__ +#define __RT5682_H__ + +#include <sound/rt5682.h> + +#define DEVICE_ID 0x6530 + +/* Info */ +#define RT5682_RESET 0x0000 +#define RT5682_VERSION_ID 0x00fd +#define RT5682_VENDOR_ID 0x00fe +#define RT5682_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5682_HP_CTRL_1 0x0002 +#define RT5682_HP_CTRL_2 0x0003 +#define RT5682_HPL_GAIN 0x0005 +#define RT5682_HPR_GAIN 0x0006 + +#define RT5682_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5682_CBJ_BST_CTRL 0x000b +#define RT5682_CBJ_CTRL_1 0x0010 +#define RT5682_CBJ_CTRL_2 0x0011 +#define RT5682_CBJ_CTRL_3 0x0012 +#define RT5682_CBJ_CTRL_4 0x0013 +#define RT5682_CBJ_CTRL_5 0x0014 +#define RT5682_CBJ_CTRL_6 0x0015 +#define RT5682_CBJ_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5682_DAC1_DIG_VOL 0x0019 +#define RT5682_STO1_ADC_DIG_VOL 0x001c +#define RT5682_STO1_ADC_BOOST 0x001f +#define RT5682_HP_IMP_GAIN_1 0x0022 +#define RT5682_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5682_SIDETONE_CTRL 0x0024 +#define RT5682_STO1_ADC_MIXER 0x0026 +#define RT5682_AD_DA_MIXER 0x0029 +#define RT5682_STO1_DAC_MIXER 0x002a +#define RT5682_A_DAC1_MUX 0x002b +#define RT5682_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5682_REC_MIXER 0x003c +#define RT5682_CAL_REC 0x0044 +#define RT5682_ALC_BACK_GAIN 0x0049 +/* Power */ +#define RT5682_PWR_DIG_1 0x0061 +#define RT5682_PWR_DIG_2 0x0062 +#define RT5682_PWR_ANLG_1 0x0063 +#define RT5682_PWR_ANLG_2 0x0064 +#define RT5682_PWR_ANLG_3 0x0065 +#define RT5682_PWR_MIXER 0x0066 +#define RT5682_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5682_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5682_RESET_LPF_CTRL 0x006c +#define RT5682_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5682_DMIC_CTRL_1 0x006e +/* Format - ADC/DAC */ +#define RT5682_I2S1_SDP 0x0070 +#define RT5682_I2S2_SDP 0x0071 +#define RT5682_ADDA_CLK_1 0x0073 +#define RT5682_ADDA_CLK_2 0x0074 +#define RT5682_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5682_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5682_TDM_CTRL 0x0079 +#define RT5682_TDM_ADDA_CTRL_1 0x007a +#define RT5682_TDM_ADDA_CTRL_2 0x007b +#define RT5682_DATA_SEL_CTRL_1 0x007c +#define RT5682_TDM_TCON_CTRL 0x007e +/* Function - Analog */ +#define RT5682_GLB_CLK 0x0080 +#define RT5682_PLL_CTRL_1 0x0081 +#define RT5682_PLL_CTRL_2 0x0082 +#define RT5682_PLL_TRACK_1 0x0083 +#define RT5682_PLL_TRACK_2 0x0084 +#define RT5682_PLL_TRACK_3 0x0085 +#define RT5682_PLL_TRACK_4 0x0086 +#define RT5682_PLL_TRACK_5 0x0087 +#define RT5682_PLL_TRACK_6 0x0088 +#define RT5682_PLL_TRACK_11 0x008c +#define RT5682_SDW_REF_CLK 0x008d +#define RT5682_DEPOP_1 0x008e +#define RT5682_DEPOP_2 0x008f +#define RT5682_HP_CHARGE_PUMP_1 0x0091 +#define RT5682_HP_CHARGE_PUMP_2 0x0092 +#define RT5682_MICBIAS_1 0x0093 +#define RT5682_MICBIAS_2 0x0094 +#define RT5682_PLL_TRACK_12 0x0098 +#define RT5682_PLL_TRACK_14 0x009a +#define RT5682_PLL2_CTRL_1 0x009b +#define RT5682_PLL2_CTRL_2 0x009c +#define RT5682_PLL2_CTRL_3 0x009d +#define RT5682_PLL2_CTRL_4 0x009e +#define RT5682_RC_CLK_CTRL 0x009f +#define RT5682_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5682_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5682_I2S2_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5682_EQ_CTRL_1 0x00ae +#define RT5682_EQ_CTRL_2 0x00af +#define RT5682_IRQ_CTRL_1 0x00b6 +#define RT5682_IRQ_CTRL_2 0x00b7 +#define RT5682_IRQ_CTRL_3 0x00b8 +#define RT5682_IRQ_CTRL_4 0x00b9 +#define RT5682_INT_ST_1 0x00be +#define RT5682_GPIO_CTRL_1 0x00c0 +#define RT5682_GPIO_CTRL_2 0x00c1 +#define RT5682_GPIO_CTRL_3 0x00c2 +#define RT5682_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5682_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5682_MID_HP_AMP_DET 0x00d2 +#define RT5682_LOW_HP_AMP_DET 0x00d3 +#define RT5682_DELAY_BUF_CTRL 0x00d4 +#define RT5682_SV_ZCD_1 0x00d9 +#define RT5682_SV_ZCD_2 0x00da +#define RT5682_IL_CMD_1 0x00db +#define RT5682_IL_CMD_2 0x00dc +#define RT5682_IL_CMD_3 0x00dd +#define RT5682_IL_CMD_4 0x00de +#define RT5682_IL_CMD_5 0x00df +#define RT5682_IL_CMD_6 0x00e0 +#define RT5682_4BTN_IL_CMD_1 0x00e2 +#define RT5682_4BTN_IL_CMD_2 0x00e3 +#define RT5682_4BTN_IL_CMD_3 0x00e4 +#define RT5682_4BTN_IL_CMD_4 0x00e5 +#define RT5682_4BTN_IL_CMD_5 0x00e6 +#define RT5682_4BTN_IL_CMD_6 0x00e7 +#define RT5682_4BTN_IL_CMD_7 0x00e8 + +#define RT5682_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5682_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5682_AJD1_CTRL 0x00f0 +#define RT5682_JD1_THD 0x00f1 +#define RT5682_JD2_THD 0x00f2 +#define RT5682_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5682_DUMMY_1 0x00fa +#define RT5682_DUMMY_2 0x00fb +#define RT5682_DUMMY_3 0x00fc + +#define RT5682_DAC_ADC_DIG_VOL1 0x0100 +#define RT5682_BIAS_CUR_CTRL_2 0x010b +#define RT5682_BIAS_CUR_CTRL_3 0x010c +#define RT5682_BIAS_CUR_CTRL_4 0x010d +#define RT5682_BIAS_CUR_CTRL_5 0x010e +#define RT5682_BIAS_CUR_CTRL_6 0x010f +#define RT5682_BIAS_CUR_CTRL_7 0x0110 +#define RT5682_BIAS_CUR_CTRL_8 0x0111 +#define RT5682_BIAS_CUR_CTRL_9 0x0112 +#define RT5682_BIAS_CUR_CTRL_10 0x0113 +#define RT5682_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5682_CHARGE_PUMP_1 0x0125 +#define RT5682_DIG_IN_CTRL_1 0x0132 +#define RT5682_PAD_DRIVING_CTRL 0x0136 +#define RT5682_SOFT_RAMP_DEPOP 0x0138 +#define RT5682_CHOP_DAC 0x013a +#define RT5682_CHOP_ADC 0x013b +#define RT5682_CALIB_ADC_CTRL 0x013c +#define RT5682_VOL_TEST 0x013f +#define RT5682_SPKVDD_DET_STA 0x0142 +#define RT5682_TEST_MODE_CTRL_1 0x0145 +#define RT5682_TEST_MODE_CTRL_2 0x0146 +#define RT5682_TEST_MODE_CTRL_3 0x0147 +#define RT5682_TEST_MODE_CTRL_4 0x0148 +#define RT5682_TEST_MODE_CTRL_5 0x0149 +#define RT5682_PLL1_INTERNAL 0x0150 +#define RT5682_PLL2_INTERNAL 0x0151 +#define RT5682_STO_NG2_CTRL_1 0x0160 +#define RT5682_STO_NG2_CTRL_2 0x0161 +#define RT5682_STO_NG2_CTRL_3 0x0162 +#define RT5682_STO_NG2_CTRL_4 0x0163 +#define RT5682_STO_NG2_CTRL_5 0x0164 +#define RT5682_STO_NG2_CTRL_6 0x0165 +#define RT5682_STO_NG2_CTRL_7 0x0166 +#define RT5682_STO_NG2_CTRL_8 0x0167 +#define RT5682_STO_NG2_CTRL_9 0x0168 +#define RT5682_STO_NG2_CTRL_10 0x0169 +#define RT5682_STO1_DAC_SIL_DET 0x0190 +#define RT5682_SIL_PSV_CTRL1 0x0194 +#define RT5682_SIL_PSV_CTRL2 0x0195 +#define RT5682_SIL_PSV_CTRL3 0x0197 +#define RT5682_SIL_PSV_CTRL4 0x0198 +#define RT5682_SIL_PSV_CTRL5 0x0199 +#define RT5682_HP_IMP_SENS_CTRL_01 0x01af +#define RT5682_HP_IMP_SENS_CTRL_02 0x01b0 +#define RT5682_HP_IMP_SENS_CTRL_03 0x01b1 +#define RT5682_HP_IMP_SENS_CTRL_04 0x01b2 +#define RT5682_HP_IMP_SENS_CTRL_05 0x01b3 +#define RT5682_HP_IMP_SENS_CTRL_06 0x01b4 +#define RT5682_HP_IMP_SENS_CTRL_07 0x01b5 +#define RT5682_HP_IMP_SENS_CTRL_08 0x01b6 +#define RT5682_HP_IMP_SENS_CTRL_09 0x01b7 +#define RT5682_HP_IMP_SENS_CTRL_10 0x01b8 +#define RT5682_HP_IMP_SENS_CTRL_11 0x01b9 +#define RT5682_HP_IMP_SENS_CTRL_12 0x01ba +#define RT5682_HP_IMP_SENS_CTRL_13 0x01bb +#define RT5682_HP_IMP_SENS_CTRL_14 0x01bc +#define RT5682_HP_IMP_SENS_CTRL_15 0x01bd +#define RT5682_HP_IMP_SENS_CTRL_16 0x01be +#define RT5682_HP_IMP_SENS_CTRL_17 0x01bf +#define RT5682_HP_IMP_SENS_CTRL_18 0x01c0 +#define RT5682_HP_IMP_SENS_CTRL_19 0x01c1 +#define RT5682_HP_IMP_SENS_CTRL_20 0x01c2 +#define RT5682_HP_IMP_SENS_CTRL_21 0x01c3 +#define RT5682_HP_IMP_SENS_CTRL_22 0x01c4 +#define RT5682_HP_IMP_SENS_CTRL_23 0x01c5 +#define RT5682_HP_IMP_SENS_CTRL_24 0x01c6 +#define RT5682_HP_IMP_SENS_CTRL_25 0x01c7 +#define RT5682_HP_IMP_SENS_CTRL_26 0x01c8 +#define RT5682_HP_IMP_SENS_CTRL_27 0x01c9 +#define RT5682_HP_IMP_SENS_CTRL_28 0x01ca +#define RT5682_HP_IMP_SENS_CTRL_29 0x01cb +#define RT5682_HP_IMP_SENS_CTRL_30 0x01cc +#define RT5682_HP_IMP_SENS_CTRL_31 0x01cd +#define RT5682_HP_IMP_SENS_CTRL_32 0x01ce +#define RT5682_HP_IMP_SENS_CTRL_33 0x01cf +#define RT5682_HP_IMP_SENS_CTRL_34 0x01d0 +#define RT5682_HP_IMP_SENS_CTRL_35 0x01d1 +#define RT5682_HP_IMP_SENS_CTRL_36 0x01d2 +#define RT5682_HP_IMP_SENS_CTRL_37 0x01d3 +#define RT5682_HP_IMP_SENS_CTRL_38 0x01d4 +#define RT5682_HP_IMP_SENS_CTRL_39 0x01d5 +#define RT5682_HP_IMP_SENS_CTRL_40 0x01d6 +#define RT5682_HP_IMP_SENS_CTRL_41 0x01d7 +#define RT5682_HP_IMP_SENS_CTRL_42 0x01d8 +#define RT5682_HP_IMP_SENS_CTRL_43 0x01d9 +#define RT5682_HP_LOGIC_CTRL_1 0x01da +#define RT5682_HP_LOGIC_CTRL_2 0x01db +#define RT5682_HP_LOGIC_CTRL_3 0x01dc +#define RT5682_HP_CALIB_CTRL_1 0x01de +#define RT5682_HP_CALIB_CTRL_2 0x01df +#define RT5682_HP_CALIB_CTRL_3 0x01e0 +#define RT5682_HP_CALIB_CTRL_4 0x01e1 +#define RT5682_HP_CALIB_CTRL_5 0x01e2 +#define RT5682_HP_CALIB_CTRL_6 0x01e3 +#define RT5682_HP_CALIB_CTRL_7 0x01e4 +#define RT5682_HP_CALIB_CTRL_9 0x01e6 +#define RT5682_HP_CALIB_CTRL_10 0x01e7 +#define RT5682_HP_CALIB_CTRL_11 0x01e8 +#define RT5682_HP_CALIB_STA_1 0x01ea +#define RT5682_HP_CALIB_STA_2 0x01eb +#define RT5682_HP_CALIB_STA_3 0x01ec +#define RT5682_HP_CALIB_STA_4 0x01ed +#define RT5682_HP_CALIB_STA_5 0x01ee +#define RT5682_HP_CALIB_STA_6 0x01ef +#define RT5682_HP_CALIB_STA_7 0x01f0 +#define RT5682_HP_CALIB_STA_8 0x01f1 +#define RT5682_HP_CALIB_STA_9 0x01f2 +#define RT5682_HP_CALIB_STA_10 0x01f3 +#define RT5682_HP_CALIB_STA_11 0x01f4 +#define RT5682_SAR_IL_CMD_1 0x0210 +#define RT5682_SAR_IL_CMD_2 0x0211 +#define RT5682_SAR_IL_CMD_3 0x0212 +#define RT5682_SAR_IL_CMD_4 0x0213 +#define RT5682_SAR_IL_CMD_5 0x0214 +#define RT5682_SAR_IL_CMD_6 0x0215 +#define RT5682_SAR_IL_CMD_7 0x0216 +#define RT5682_SAR_IL_CMD_8 0x0217 +#define RT5682_SAR_IL_CMD_9 0x0218 +#define RT5682_SAR_IL_CMD_10 0x0219 +#define RT5682_SAR_IL_CMD_11 0x021a +#define RT5682_SAR_IL_CMD_12 0x021b +#define RT5682_SAR_IL_CMD_13 0x021c +#define RT5682_EFUSE_CTRL_1 0x0250 +#define RT5682_EFUSE_CTRL_2 0x0251 +#define RT5682_EFUSE_CTRL_3 0x0252 +#define RT5682_EFUSE_CTRL_4 0x0253 +#define RT5682_EFUSE_CTRL_5 0x0254 +#define RT5682_EFUSE_CTRL_6 0x0255 +#define RT5682_EFUSE_CTRL_7 0x0256 +#define RT5682_EFUSE_CTRL_8 0x0257 +#define RT5682_EFUSE_CTRL_9 0x0258 +#define RT5682_EFUSE_CTRL_10 0x0259 +#define RT5682_EFUSE_CTRL_11 0x025a +#define RT5682_JD_TOP_VC_VTRL 0x0270 +#define RT5682_DRC1_CTRL_0 0x02ff +#define RT5682_DRC1_CTRL_1 0x0300 +#define RT5682_DRC1_CTRL_2 0x0301 +#define RT5682_DRC1_CTRL_3 0x0302 +#define RT5682_DRC1_CTRL_4 0x0303 +#define RT5682_DRC1_CTRL_5 0x0304 +#define RT5682_DRC1_CTRL_6 0x0305 +#define RT5682_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5682_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5682_DRC1_PRIV_1 0x0310 +#define RT5682_DRC1_PRIV_2 0x0311 +#define RT5682_DRC1_PRIV_3 0x0312 +#define RT5682_DRC1_PRIV_4 0x0313 +#define RT5682_DRC1_PRIV_5 0x0314 +#define RT5682_DRC1_PRIV_6 0x0315 +#define RT5682_DRC1_PRIV_7 0x0316 +#define RT5682_DRC1_PRIV_8 0x0317 +#define RT5682_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5682_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5682_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5682_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5682_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5682_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5682_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5682_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5682_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5682_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5682_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5682_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5682_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5682_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5682_R_EQ_LPF1_A1 0x03d1 +#define RT5682_L_EQ_LPF1_H0 0x03d2 +#define RT5682_R_EQ_LPF1_H0 0x03d3 +#define RT5682_L_EQ_BPF1_A1 0x03d4 +#define RT5682_R_EQ_BPF1_A1 0x03d5 +#define RT5682_L_EQ_BPF1_A2 0x03d6 +#define RT5682_R_EQ_BPF1_A2 0x03d7 +#define RT5682_L_EQ_BPF1_H0 0x03d8 +#define RT5682_R_EQ_BPF1_H0 0x03d9 +#define RT5682_L_EQ_BPF2_A1 0x03da +#define RT5682_R_EQ_BPF2_A1 0x03db +#define RT5682_L_EQ_BPF2_A2 0x03dc +#define RT5682_R_EQ_BPF2_A2 0x03dd +#define RT5682_L_EQ_BPF2_H0 0x03de +#define RT5682_R_EQ_BPF2_H0 0x03df +#define RT5682_L_EQ_BPF3_A1 0x03e0 +#define RT5682_R_EQ_BPF3_A1 0x03e1 +#define RT5682_L_EQ_BPF3_A2 0x03e2 +#define RT5682_R_EQ_BPF3_A2 0x03e3 +#define RT5682_L_EQ_BPF3_H0 0x03e4 +#define RT5682_R_EQ_BPF3_H0 0x03e5 +#define RT5682_L_EQ_BPF4_A1 0x03e6 +#define RT5682_R_EQ_BPF4_A1 0x03e7 +#define RT5682_L_EQ_BPF4_A2 0x03e8 +#define RT5682_R_EQ_BPF4_A2 0x03e9 +#define RT5682_L_EQ_BPF4_H0 0x03ea +#define RT5682_R_EQ_BPF4_H0 0x03eb +#define RT5682_L_EQ_HPF1_A1 0x03ec +#define RT5682_R_EQ_HPF1_A1 0x03ed +#define RT5682_L_EQ_HPF1_H0 0x03ee +#define RT5682_R_EQ_HPF1_H0 0x03ef +#define RT5682_L_EQ_PRE_VOL 0x03f0 +#define RT5682_R_EQ_PRE_VOL 0x03f1 +#define RT5682_L_EQ_POST_VOL 0x03f2 +#define RT5682_R_EQ_POST_VOL 0x03f3 +#define RT5682_I2C_MODE 0xffff + + +/* global definition */ +#define RT5682_L_MUTE (0x1 << 15) +#define RT5682_L_MUTE_SFT 15 +#define RT5682_VOL_L_MUTE (0x1 << 14) +#define RT5682_VOL_L_SFT 14 +#define RT5682_R_MUTE (0x1 << 7) +#define RT5682_R_MUTE_SFT 7 +#define RT5682_VOL_R_MUTE (0x1 << 6) +#define RT5682_VOL_R_SFT 6 +#define RT5682_L_VOL_MASK (0x3f << 8) +#define RT5682_L_VOL_SFT 8 +#define RT5682_R_VOL_MASK (0x3f) +#define RT5682_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5682_G_HP (0xf << 8) +#define RT5682_G_HP_SFT 8 +#define RT5682_G_STO_DA_DMIX (0xf) +#define RT5682_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5682_BST_CBJ_MASK (0xf << 8) +#define RT5682_BST_CBJ_SFT 8 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5682_EMB_JD_EN (0x1 << 15) +#define RT5682_EMB_JD_EN_SFT 15 +#define RT5682_EMB_JD_RST (0x1 << 14) +#define RT5682_JD_MODE (0x1 << 13) +#define RT5682_JD_MODE_SFT 13 +#define RT5682_DET_TYPE (0x1 << 12) +#define RT5682_DET_TYPE_SFT 12 +#define RT5682_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5682_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5682_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5682_EXT_JD_DIG (0x1 << 9) +#define RT5682_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5682_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5682_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5682_FAST_OFF_MASK (0x1 << 7) +#define RT5682_FAST_OFF_EN (0x1 << 7) +#define RT5682_FAST_OFF_DIS (0x0 << 7) +#define RT5682_VREF_POW_MASK (0x1 << 6) +#define RT5682_VREF_POW_FSM (0x0 << 6) +#define RT5682_VREF_POW_REG (0x1 << 6) +#define RT5682_MB1_PATH_MASK (0x1 << 5) +#define RT5682_CTRL_MB1_REG (0x1 << 5) +#define RT5682_CTRL_MB1_FSM (0x0 << 5) +#define RT5682_MB2_PATH_MASK (0x1 << 4) +#define RT5682_CTRL_MB2_REG (0x1 << 4) +#define RT5682_CTRL_MB2_FSM (0x0 << 4) +#define RT5682_TRIG_JD_MASK (0x1 << 3) +#define RT5682_TRIG_JD_HIGH (0x1 << 3) +#define RT5682_TRIG_JD_LOW (0x0 << 3) +#define RT5682_MIC_CAP_MASK (0x1 << 1) +#define RT5682_MIC_CAP_HS (0x1 << 1) +#define RT5682_MIC_CAP_HP (0x0 << 1) +#define RT5682_MIC_CAP_SRC_MASK (0x1) +#define RT5682_MIC_CAP_SRC_REG (0x1) +#define RT5682_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5682_EXT_JD_SRC (0x7 << 4) +#define RT5682_EXT_JD_SRC_SFT 4 +#define RT5682_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5682_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5682_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5682_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5682_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5682_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 3 (0x0012) */ +#define RT5682_CBJ_IN_BUF_EN (0x1 << 7) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5682_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5682_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5682_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5682_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5682_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5682_CBJ_JD_TEST_MODE (0x1 << 6) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5682_DAC_L1_VOL_MASK (0xff << 8) +#define RT5682_DAC_L1_VOL_SFT 8 +#define RT5682_DAC_R1_VOL_MASK (0xff) +#define RT5682_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5682_ADC_L_VOL_MASK (0x7f << 8) +#define RT5682_ADC_L_VOL_SFT 8 +#define RT5682_ADC_R_VOL_MASK (0x7f) +#define RT5682_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5682_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5682_STO1_ADC_L_BST_SFT 14 +#define RT5682_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5682_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5682_ST_SRC_SEL (0x1 << 8) +#define RT5682_ST_SRC_SFT 8 +#define RT5682_ST_EN_MASK (0x1 << 6) +#define RT5682_ST_DIS (0x0 << 6) +#define RT5682_ST_EN (0x1 << 6) +#define RT5682_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5682_M_STO1_ADC_L1 (0x1 << 15) +#define RT5682_M_STO1_ADC_L1_SFT 15 +#define RT5682_M_STO1_ADC_L2 (0x1 << 14) +#define RT5682_M_STO1_ADC_L2_SFT 14 +#define RT5682_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5682_STO1_ADC1L_SRC_SFT 13 +#define RT5682_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5682_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5682_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5682_STO1_ADC2L_SRC_SFT 12 +#define RT5682_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5682_STO1_ADCL_SRC_SFT 10 +#define RT5682_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5682_STO1_DD_L_SRC_SFT 9 +#define RT5682_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_SFT 8 +#define RT5682_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5682_M_STO1_ADC_R1 (0x1 << 7) +#define RT5682_M_STO1_ADC_R1_SFT 7 +#define RT5682_M_STO1_ADC_R2 (0x1 << 6) +#define RT5682_M_STO1_ADC_R2_SFT 6 +#define RT5682_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5682_STO1_ADC1R_SRC_SFT 5 +#define RT5682_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5682_STO1_ADC2R_SRC_SFT 4 +#define RT5682_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5682_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5682_M_ADCMIX_L (0x1 << 15) +#define RT5682_M_ADCMIX_L_SFT 15 +#define RT5682_M_DAC1_L (0x1 << 14) +#define RT5682_M_DAC1_L_SFT 14 +#define RT5682_DAC1_R_SEL_MASK (0x1 << 10) +#define RT5682_DAC1_R_SEL_SFT 10 +#define RT5682_DAC1_L_SEL_MASK (0x1 << 8) +#define RT5682_DAC1_L_SEL_SFT 8 +#define RT5682_M_ADCMIX_R (0x1 << 7) +#define RT5682_M_ADCMIX_R_SFT 7 +#define RT5682_M_DAC1_R (0x1 << 6) +#define RT5682_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5682_M_DAC_L1_STO_L (0x1 << 15) +#define RT5682_M_DAC_L1_STO_L_SFT 15 +#define RT5682_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5682_G_DAC_L1_STO_L_SFT 14 +#define RT5682_M_DAC_R1_STO_L (0x1 << 13) +#define RT5682_M_DAC_R1_STO_L_SFT 13 +#define RT5682_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5682_G_DAC_R1_STO_L_SFT 12 +#define RT5682_M_DAC_L1_STO_R (0x1 << 7) +#define RT5682_M_DAC_L1_STO_R_SFT 7 +#define RT5682_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5682_G_DAC_L1_STO_R_SFT 6 +#define RT5682_M_DAC_R1_STO_R (0x1 << 5) +#define RT5682_M_DAC_R1_STO_R_SFT 5 +#define RT5682_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5682_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5682_M_ST_STO_L (0x1 << 9) +#define RT5682_M_ST_STO_L_SFT 9 +#define RT5682_M_ST_STO_R (0x1 << 8) +#define RT5682_M_ST_STO_R_SFT 8 +#define RT5682_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5682_A_DACL1_SFT 4 +#define RT5682_DAC_R1_SRC_MASK (0x3) +#define RT5682_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5682_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5682_IF2_ADC_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5682_G_CBJ_RM1_L (0x7 << 10) +#define RT5682_G_CBJ_RM1_L_SFT 10 +#define RT5682_M_CBJ_RM1_L (0x1 << 7) +#define RT5682_M_CBJ_RM1_L_SFT 7 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5682_PWR_I2S1 (0x1 << 15) +#define RT5682_PWR_I2S1_BIT 15 +#define RT5682_PWR_I2S2 (0x1 << 14) +#define RT5682_PWR_I2S2_BIT 14 +#define RT5682_PWR_DAC_L1 (0x1 << 11) +#define RT5682_PWR_DAC_L1_BIT 11 +#define RT5682_PWR_DAC_R1 (0x1 << 10) +#define RT5682_PWR_DAC_R1_BIT 10 +#define RT5682_PWR_LDO (0x1 << 8) +#define RT5682_PWR_LDO_BIT 8 +#define RT5682_PWR_ADC_L1 (0x1 << 4) +#define RT5682_PWR_ADC_L1_BIT 4 +#define RT5682_PWR_ADC_R1 (0x1 << 3) +#define RT5682_PWR_ADC_R1_BIT 3 +#define RT5682_DIG_GATE_CTRL (0x1 << 0) +#define RT5682_DIG_GATE_CTRL_SFT 0 + + +/* Power Management for Digital 2 (0x0062) */ +#define RT5682_PWR_ADC_S1F (0x1 << 15) +#define RT5682_PWR_ADC_S1F_BIT 15 +#define RT5682_PWR_DAC_S1F (0x1 << 10) +#define RT5682_PWR_DAC_S1F_BIT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5682_PWR_VREF1 (0x1 << 15) +#define RT5682_PWR_VREF1_BIT 15 +#define RT5682_PWR_FV1 (0x1 << 14) +#define RT5682_PWR_FV1_BIT 14 +#define RT5682_PWR_VREF2 (0x1 << 13) +#define RT5682_PWR_VREF2_BIT 13 +#define RT5682_PWR_FV2 (0x1 << 12) +#define RT5682_PWR_FV2_BIT 12 +#define RT5682_LDO1_DBG_MASK (0x3 << 10) +#define RT5682_PWR_MB (0x1 << 9) +#define RT5682_PWR_MB_BIT 9 +#define RT5682_PWR_BG (0x1 << 7) +#define RT5682_PWR_BG_BIT 7 +#define RT5682_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5682_LDO1_BYPASS (0x1 << 6) +#define RT5682_LDO1_NOT_BYPASS (0x0 << 6) +#define RT5682_PWR_MA_BIT 6 +#define RT5682_LDO1_DVO_MASK (0x3 << 4) +#define RT5682_LDO1_DVO_09 (0x0 << 4) +#define RT5682_LDO1_DVO_10 (0x1 << 4) +#define RT5682_LDO1_DVO_12 (0x2 << 4) +#define RT5682_LDO1_DVO_14 (0x3 << 4) +#define RT5682_HP_DRIVER_MASK (0x3 << 2) +#define RT5682_HP_DRIVER_1X (0x0 << 2) +#define RT5682_HP_DRIVER_3X (0x1 << 2) +#define RT5682_HP_DRIVER_5X (0x3 << 2) +#define RT5682_PWR_HA_L (0x1 << 1) +#define RT5682_PWR_HA_L_BIT 1 +#define RT5682_PWR_HA_R (0x1 << 0) +#define RT5682_PWR_HA_R_BIT 0 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5682_PWR_MB1 (0x1 << 11) +#define RT5682_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5682_PWR_MB1_BIT 11 +#define RT5682_PWR_MB2 (0x1 << 10) +#define RT5682_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5682_PWR_MB2_BIT 10 +#define RT5682_PWR_JDH (0x1 << 3) +#define RT5682_PWR_JDH_BIT 3 +#define RT5682_PWR_JDL (0x1 << 2) +#define RT5682_PWR_JDL_BIT 2 +#define RT5682_PWR_RM1_L (0x1 << 1) +#define RT5682_PWR_RM1_L_BIT 1 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5682_PWR_CBJ (0x1 << 9) +#define RT5682_PWR_CBJ_BIT 9 +#define RT5682_PWR_PLL (0x1 << 6) +#define RT5682_PWR_PLL_BIT 6 +#define RT5682_PWR_PLL2B (0x1 << 5) +#define RT5682_PWR_PLL2B_BIT 5 +#define RT5682_PWR_PLL2F (0x1 << 4) +#define RT5682_PWR_PLL2F_BIT 4 +#define RT5682_PWR_LDO2 (0x1 << 2) +#define RT5682_PWR_LDO2_BIT 2 +#define RT5682_PWR_DET_SPKVDD (0x1 << 1) +#define RT5682_PWR_DET_SPKVDD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5682_PWR_STO1_DAC_L (0x1 << 5) +#define RT5682_PWR_STO1_DAC_L_BIT 5 +#define RT5682_PWR_STO1_DAC_R (0x1 << 4) +#define RT5682_PWR_STO1_DAC_R_BIT 4 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5682_SYS_CLK_DET (0x1 << 15) +#define RT5682_SYS_CLK_DET_SFT 15 +#define RT5682_PLL1_CLK_DET (0x1 << 14) +#define RT5682_PLL1_CLK_DET_SFT 14 +#define RT5682_PLL2_CLK_DET (0x1 << 13) +#define RT5682_PLL2_CLK_DET_SFT 13 +#define RT5682_POW_CLK_DET2_SFT 8 +#define RT5682_POW_CLK_DET_SFT 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5682_DMIC_1_EN_MASK (0x1 << 15) +#define RT5682_DMIC_1_EN_SFT 15 +#define RT5682_DMIC_1_DIS (0x0 << 15) +#define RT5682_DMIC_1_EN (0x1 << 15) +#define RT5682_DMIC_1_DP_MASK (0x3 << 4) +#define RT5682_DMIC_1_DP_SFT 4 +#define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5682_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5682_DMIC_CLK_MASK (0xf << 0) +#define RT5682_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5682_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5682_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5682_SEL_ADCDAT_IN (0x1 << 15) +#define RT5682_SEL_ADCDAT_SFT 15 +#define RT5682_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5682_I2S1_TX_CHL_SFT 12 +#define RT5682_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5682_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5682_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5682_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5682_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5682_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5682_I2S1_RX_CHL_SFT 8 +#define RT5682_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5682_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5682_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5682_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5682_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5682_I2S1_MONO_MASK (0x1 << 7) +#define RT5682_I2S1_MONO_EN (0x1 << 7) +#define RT5682_I2S1_MONO_DIS (0x0 << 7) +#define RT5682_I2S2_MONO_MASK (0x1 << 6) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S1_DL_MASK (0x7 << 4) +#define RT5682_I2S1_DL_SFT 4 +#define RT5682_I2S1_DL_16 (0x0 << 4) +#define RT5682_I2S1_DL_20 (0x1 << 4) +#define RT5682_I2S1_DL_24 (0x2 << 4) +#define RT5682_I2S1_DL_32 (0x3 << 4) +#define RT5682_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */ +#define RT5682_I2S2_MS_MASK (0x1 << 15) +#define RT5682_I2S2_MS_SFT 15 +#define RT5682_I2S2_MS_M (0x0 << 15) +#define RT5682_I2S2_MS_S (0x1 << 15) +#define RT5682_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5682_I2S2_PIN_CFG_SFT 14 +#define RT5682_I2S2_CLK_SEL_MASK (0x1 << 11) +#define RT5682_I2S2_CLK_SEL_SFT 11 +#define RT5682_I2S2_OUT_MASK (0x1 << 9) +#define RT5682_I2S2_OUT_SFT 9 +#define RT5682_I2S2_OUT_UM (0x0 << 9) +#define RT5682_I2S2_OUT_M (0x1 << 9) +#define RT5682_I2S_BP_MASK (0x1 << 8) +#define RT5682_I2S_BP_SFT 8 +#define RT5682_I2S_BP_NOR (0x0 << 8) +#define RT5682_I2S_BP_INV (0x1 << 8) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S2_DL_MASK (0x3 << 4) +#define RT5682_I2S2_DL_SFT 4 +#define RT5682_I2S2_DL_16 (0x0 << 4) +#define RT5682_I2S2_DL_20 (0x1 << 4) +#define RT5682_I2S2_DL_24 (0x2 << 4) +#define RT5682_I2S2_DL_8 (0x3 << 4) +#define RT5682_I2S_DF_MASK (0x7) +#define RT5682_I2S_DF_SFT 0 +#define RT5682_I2S_DF_I2S (0x0) +#define RT5682_I2S_DF_LEFT (0x1) +#define RT5682_I2S_DF_PCM_A (0x2) +#define RT5682_I2S_DF_PCM_B (0x3) +#define RT5682_I2S_DF_PCM_A_N (0x6) +#define RT5682_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5682_ADC_OSR_MASK (0xf << 12) +#define RT5682_ADC_OSR_SFT 12 +#define RT5682_ADC_OSR_D_1 (0x0 << 12) +#define RT5682_ADC_OSR_D_2 (0x1 << 12) +#define RT5682_ADC_OSR_D_4 (0x2 << 12) +#define RT5682_ADC_OSR_D_6 (0x3 << 12) +#define RT5682_ADC_OSR_D_8 (0x4 << 12) +#define RT5682_ADC_OSR_D_12 (0x5 << 12) +#define RT5682_ADC_OSR_D_16 (0x6 << 12) +#define RT5682_ADC_OSR_D_24 (0x7 << 12) +#define RT5682_ADC_OSR_D_32 (0x8 << 12) +#define RT5682_ADC_OSR_D_48 (0x9 << 12) +#define RT5682_I2S_M_DIV_MASK (0xf << 12) +#define RT5682_I2S_M_DIV_SFT 8 +#define RT5682_I2S_M_D_1 (0x0 << 8) +#define RT5682_I2S_M_D_2 (0x1 << 8) +#define RT5682_I2S_M_D_3 (0x2 << 8) +#define RT5682_I2S_M_D_4 (0x3 << 8) +#define RT5682_I2S_M_D_6 (0x4 << 8) +#define RT5682_I2S_M_D_8 (0x5 << 8) +#define RT5682_I2S_M_D_12 (0x6 << 8) +#define RT5682_I2S_M_D_16 (0x7 << 8) +#define RT5682_I2S_M_D_24 (0x8 << 8) +#define RT5682_I2S_M_D_32 (0x9 << 8) +#define RT5682_I2S_M_D_48 (0x10 << 8) +#define RT5682_I2S_CLK_SRC_MASK (0x7 << 4) +#define RT5682_I2S_CLK_SRC_SFT 4 +#define RT5682_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5682_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5682_I2S_CLK_SRC_PLL2 (0x2 << 4) +#define RT5682_I2S_CLK_SRC_SDW (0x3 << 4) +#define RT5682_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */ +#define RT5682_DAC_OSR_MASK (0xf << 0) +#define RT5682_DAC_OSR_SFT 0 +#define RT5682_DAC_OSR_D_1 (0x0 << 0) +#define RT5682_DAC_OSR_D_2 (0x1 << 0) +#define RT5682_DAC_OSR_D_4 (0x2 << 0) +#define RT5682_DAC_OSR_D_6 (0x3 << 0) +#define RT5682_DAC_OSR_D_8 (0x4 << 0) +#define RT5682_DAC_OSR_D_12 (0x5 << 0) +#define RT5682_DAC_OSR_D_16 (0x6 << 0) +#define RT5682_DAC_OSR_D_24 (0x7 << 0) +#define RT5682_DAC_OSR_D_32 (0x8 << 0) +#define RT5682_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5682_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5682_I2S2_BCLK_MS2_SFT 11 +#define RT5682_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5682_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5682_TDM_TX_CH_MASK (0x3 << 12) +#define RT5682_TDM_TX_CH_2 (0x0 << 12) +#define RT5682_TDM_TX_CH_4 (0x1 << 12) +#define RT5682_TDM_TX_CH_6 (0x2 << 12) +#define RT5682_TDM_TX_CH_8 (0x3 << 12) +#define RT5682_TDM_RX_CH_MASK (0x3 << 8) +#define RT5682_TDM_RX_CH_2 (0x0 << 8) +#define RT5682_TDM_RX_CH_4 (0x1 << 8) +#define RT5682_TDM_RX_CH_6 (0x2 << 8) +#define RT5682_TDM_RX_CH_8 (0x3 << 8) +#define RT5682_TDM_ADC_LCA_MASK (0xf << 4) +#define RT5682_TDM_ADC_LCA_SFT 4 +#define RT5682_TDM_ADC_DL_SFT 0 + +/* TDM control 2 (0x007a) */ +#define RT5682_IF1_ADC1_SEL_SFT 14 +#define RT5682_IF1_ADC2_SEL_SFT 12 +#define RT5682_IF1_ADC3_SEL_SFT 10 +#define RT5682_IF1_ADC4_SEL_SFT 8 +#define RT5682_TDM_ADC_SEL_SFT 4 + +/* TDM control 3 (0x007b) */ +#define RT5682_TDM_EN (0x1 << 7) + +/* TDM/I2S control (0x007e) */ +#define RT5682_TDM_S_BP_MASK (0x1 << 15) +#define RT5682_TDM_S_BP_SFT 15 +#define RT5682_TDM_S_BP_NOR (0x0 << 15) +#define RT5682_TDM_S_BP_INV (0x1 << 15) +#define RT5682_TDM_S_LP_MASK (0x1 << 14) +#define RT5682_TDM_S_LP_SFT 14 +#define RT5682_TDM_S_LP_NOR (0x0 << 14) +#define RT5682_TDM_S_LP_INV (0x1 << 14) +#define RT5682_TDM_DF_MASK (0x7 << 11) +#define RT5682_TDM_DF_SFT 11 +#define RT5682_TDM_DF_I2S (0x0 << 11) +#define RT5682_TDM_DF_LEFT (0x1 << 11) +#define RT5682_TDM_DF_PCM_A (0x2 << 11) +#define RT5682_TDM_DF_PCM_B (0x3 << 11) +#define RT5682_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5682_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5682_TDM_CL_MASK (0x3 << 4) +#define RT5682_TDM_CL_16 (0x0 << 4) +#define RT5682_TDM_CL_20 (0x1 << 4) +#define RT5682_TDM_CL_24 (0x2 << 4) +#define RT5682_TDM_CL_32 (0x3 << 4) +#define RT5682_TDM_M_BP_MASK (0x1 << 2) +#define RT5682_TDM_M_BP_SFT 2 +#define RT5682_TDM_M_BP_NOR (0x0 << 2) +#define RT5682_TDM_M_BP_INV (0x1 << 2) +#define RT5682_TDM_M_LP_MASK (0x1 << 1) +#define RT5682_TDM_M_LP_SFT 1 +#define RT5682_TDM_M_LP_NOR (0x0 << 1) +#define RT5682_TDM_M_LP_INV (0x1 << 1) +#define RT5682_TDM_MS_MASK (0x1 << 0) +#define RT5682_TDM_MS_SFT 0 +#define RT5682_TDM_MS_M (0x0 << 0) +#define RT5682_TDM_MS_S (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5682_SCLK_SRC_MASK (0x7 << 13) +#define RT5682_SCLK_SRC_SFT 13 +#define RT5682_SCLK_SRC_MCLK (0x0 << 13) +#define RT5682_SCLK_SRC_PLL1 (0x1 << 13) +#define RT5682_SCLK_SRC_PLL2 (0x2 << 13) +#define RT5682_SCLK_SRC_SDW (0x3 << 13) +#define RT5682_SCLK_SRC_RCCLK (0x4 << 13) +#define RT5682_PLL1_SRC_MASK (0x3 << 10) +#define RT5682_PLL1_SRC_SFT 10 +#define RT5682_PLL1_SRC_MCLK (0x0 << 10) +#define RT5682_PLL1_SRC_BCLK1 (0x1 << 10) +#define RT5682_PLL1_SRC_SDW (0x2 << 10) +#define RT5682_PLL1_SRC_RC (0x3 << 10) +#define RT5682_PLL2_SRC_MASK (0x3 << 8) +#define RT5682_PLL2_SRC_SFT 8 +#define RT5682_PLL2_SRC_MCLK (0x0 << 8) +#define RT5682_PLL2_SRC_BCLK1 (0x1 << 8) +#define RT5682_PLL2_SRC_SDW (0x2 << 8) +#define RT5682_PLL2_SRC_RC (0x3 << 8) + + + +#define RT5682_PLL_INP_MAX 40000000 +#define RT5682_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5682_PLL_N_MAX 0x001ff +#define RT5682_PLL_N_MASK (RT5682_PLL_N_MAX << 7) +#define RT5682_PLL_N_SFT 7 +#define RT5682_PLL_K_MAX 0x001f +#define RT5682_PLL_K_MASK (RT5682_PLL_K_MAX) +#define RT5682_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5682_PLL_M_MAX 0x00f +#define RT5682_PLL_M_MASK (RT5682_PLL_M_MAX << 12) +#define RT5682_PLL_M_SFT 12 +#define RT5682_PLL_M_BP (0x1 << 11) +#define RT5682_PLL_M_BP_SFT 11 +#define RT5682_PLL_K_BP (0x1 << 10) +#define RT5682_PLL_K_BP_SFT 10 +#define RT5682_PLL_RST (0x1 << 1) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5682_DA_ASRC_MASK (0x1 << 13) +#define RT5682_DA_ASRC_SFT 13 +#define RT5682_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5682_DAC_STO1_ASRC_SFT 12 +#define RT5682_AD_ASRC_MASK (0x1 << 8) +#define RT5682_AD_ASRC_SFT 8 +#define RT5682_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5682_AD_ASRC_SEL_SFT 4 +#define RT5682_DMIC_ASRC_MASK (0x1 << 3) +#define RT5682_DMIC_ASRC_SFT 3 +#define RT5682_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5682_ADC_STO1_ASRC_SFT 2 +#define RT5682_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5682_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5682_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5682_FILTER_CLK_SEL_SFT 12 +#define RT5682_FILTER_CLK_DIV_MASK (0xf << 8) +#define RT5682_FILTER_CLK_DIV_SFT 8 + +/* ASRC Control 4 (0x0086) */ +#define RT5682_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5682_ASRCIN_FTK_N1_SFT 14 +#define RT5682_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5682_ASRCIN_FTK_N2_SFT 12 +#define RT5682_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5682_ASRCIN_FTK_M1_SFT 8 +#define RT5682_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5682_ASRCIN_FTK_M2_SFT 4 + +/* SoundWire reference clk (0x008d) */ +#define RT5682_PLL2_OUT_MASK (0x1 << 8) +#define RT5682_PLL2_OUT_98M (0x0 << 8) +#define RT5682_PLL2_OUT_49M (0x1 << 8) +#define RT5682_SDW_REF_2_MASK (0xf << 4) +#define RT5682_SDW_REF_2_SFT 4 +#define RT5682_SDW_REF_2_48K (0x0 << 4) +#define RT5682_SDW_REF_2_96K (0x1 << 4) +#define RT5682_SDW_REF_2_192K (0x2 << 4) +#define RT5682_SDW_REF_2_32K (0x3 << 4) +#define RT5682_SDW_REF_2_24K (0x4 << 4) +#define RT5682_SDW_REF_2_16K (0x5 << 4) +#define RT5682_SDW_REF_2_12K (0x6 << 4) +#define RT5682_SDW_REF_2_8K (0x7 << 4) +#define RT5682_SDW_REF_2_44K (0x8 << 4) +#define RT5682_SDW_REF_2_88K (0x9 << 4) +#define RT5682_SDW_REF_2_176K (0xa << 4) +#define RT5682_SDW_REF_2_353K (0xb << 4) +#define RT5682_SDW_REF_2_22K (0xc << 4) +#define RT5682_SDW_REF_2_384K (0xd << 4) +#define RT5682_SDW_REF_2_11K (0xe << 4) +#define RT5682_SDW_REF_1_MASK (0xf << 0) +#define RT5682_SDW_REF_1_SFT 0 +#define RT5682_SDW_REF_1_48K (0x0 << 0) +#define RT5682_SDW_REF_1_96K (0x1 << 0) +#define RT5682_SDW_REF_1_192K (0x2 << 0) +#define RT5682_SDW_REF_1_32K (0x3 << 0) +#define RT5682_SDW_REF_1_24K (0x4 << 0) +#define RT5682_SDW_REF_1_16K (0x5 << 0) +#define RT5682_SDW_REF_1_12K (0x6 << 0) +#define RT5682_SDW_REF_1_8K (0x7 << 0) +#define RT5682_SDW_REF_1_44K (0x8 << 0) +#define RT5682_SDW_REF_1_88K (0x9 << 0) +#define RT5682_SDW_REF_1_176K (0xa << 0) +#define RT5682_SDW_REF_1_353K (0xb << 0) +#define RT5682_SDW_REF_1_22K (0xc << 0) +#define RT5682_SDW_REF_1_384K (0xd << 0) +#define RT5682_SDW_REF_1_11K (0xe << 0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5682_PUMP_EN (0x1 << 3) +#define RT5682_PUMP_EN_SFT 3 +#define RT5682_CAPLESS_EN (0x1 << 0) +#define RT5682_CAPLESS_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5682_RAMP_MASK (0x1 << 12) +#define RT5682_RAMP_SFT 12 +#define RT5682_RAMP_DIS (0x0 << 12) +#define RT5682_RAMP_EN (0x1 << 12) +#define RT5682_BPS_MASK (0x1 << 11) +#define RT5682_BPS_SFT 11 +#define RT5682_BPS_DIS (0x0 << 11) +#define RT5682_BPS_EN (0x1 << 11) +#define RT5682_FAST_UPDN_MASK (0x1 << 10) +#define RT5682_FAST_UPDN_SFT 10 +#define RT5682_FAST_UPDN_DIS (0x0 << 10) +#define RT5682_FAST_UPDN_EN (0x1 << 10) +#define RT5682_VLO_MASK (0x1 << 7) +#define RT5682_VLO_SFT 7 +#define RT5682_VLO_3V (0x0 << 7) +#define RT5682_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5682_OSW_L_MASK (0x1 << 11) +#define RT5682_OSW_L_SFT 11 +#define RT5682_OSW_L_DIS (0x0 << 11) +#define RT5682_OSW_L_EN (0x1 << 11) +#define RT5682_OSW_R_MASK (0x1 << 10) +#define RT5682_OSW_R_SFT 10 +#define RT5682_OSW_R_DIS (0x0 << 10) +#define RT5682_OSW_R_EN (0x1 << 10) +#define RT5682_PM_HP_MASK (0x3 << 8) +#define RT5682_PM_HP_SFT 8 +#define RT5682_PM_HP_LV (0x0 << 8) +#define RT5682_PM_HP_MV (0x1 << 8) +#define RT5682_PM_HP_HV (0x2 << 8) +#define RT5682_IB_HP_MASK (0x3 << 6) +#define RT5682_IB_HP_SFT 6 +#define RT5682_IB_HP_125IL (0x0 << 6) +#define RT5682_IB_HP_25IL (0x1 << 6) +#define RT5682_IB_HP_5IL (0x2 << 6) +#define RT5682_IB_HP_1IL (0x3 << 6) + +/* Micbias Control1 (0x93) */ +#define RT5682_MIC1_OV_MASK (0x3 << 14) +#define RT5682_MIC1_OV_SFT 14 +#define RT5682_MIC1_OV_2V7 (0x0 << 14) +#define RT5682_MIC1_OV_2V4 (0x1 << 14) +#define RT5682_MIC1_OV_2V25 (0x3 << 14) +#define RT5682_MIC1_OV_1V8 (0x4 << 14) +#define RT5682_MIC1_CLK_MASK (0x1 << 13) +#define RT5682_MIC1_CLK_SFT 13 +#define RT5682_MIC1_CLK_DIS (0x0 << 13) +#define RT5682_MIC1_CLK_EN (0x1 << 13) +#define RT5682_MIC1_OVCD_MASK (0x1 << 12) +#define RT5682_MIC1_OVCD_SFT 12 +#define RT5682_MIC1_OVCD_DIS (0x0 << 12) +#define RT5682_MIC1_OVCD_EN (0x1 << 12) +#define RT5682_MIC1_OVTH_MASK (0x3 << 10) +#define RT5682_MIC1_OVTH_SFT 10 +#define RT5682_MIC1_OVTH_768UA (0x0 << 10) +#define RT5682_MIC1_OVTH_960UA (0x1 << 10) +#define RT5682_MIC1_OVTH_1152UA (0x2 << 10) +#define RT5682_MIC1_OVTH_1960UA (0x3 << 10) +#define RT5682_MIC2_OV_MASK (0x3 << 8) +#define RT5682_MIC2_OV_SFT 8 +#define RT5682_MIC2_OV_2V7 (0x0 << 8) +#define RT5682_MIC2_OV_2V4 (0x1 << 8) +#define RT5682_MIC2_OV_2V25 (0x3 << 8) +#define RT5682_MIC2_OV_1V8 (0x4 << 8) +#define RT5682_MIC2_CLK_MASK (0x1 << 7) +#define RT5682_MIC2_CLK_SFT 7 +#define RT5682_MIC2_CLK_DIS (0x0 << 7) +#define RT5682_MIC2_CLK_EN (0x1 << 7) +#define RT5682_MIC2_OVTH_MASK (0x3 << 4) +#define RT5682_MIC2_OVTH_SFT 4 +#define RT5682_MIC2_OVTH_768UA (0x0 << 4) +#define RT5682_MIC2_OVTH_960UA (0x1 << 4) +#define RT5682_MIC2_OVTH_1152UA (0x2 << 4) +#define RT5682_MIC2_OVTH_1960UA (0x3 << 4) +#define RT5682_PWR_MB_MASK (0x1 << 3) +#define RT5682_PWR_MB_SFT 3 +#define RT5682_PWR_MB_PD (0x0 << 3) +#define RT5682_PWR_MB_PU (0x1 << 3) + +/* Micbias Control2 (0x0094) */ +#define RT5682_PWR_CLK25M_MASK (0x1 << 9) +#define RT5682_PWR_CLK25M_SFT 9 +#define RT5682_PWR_CLK25M_PD (0x0 << 9) +#define RT5682_PWR_CLK25M_PU (0x1 << 9) +#define RT5682_PWR_CLK1M_MASK (0x1 << 8) +#define RT5682_PWR_CLK1M_SFT 8 +#define RT5682_PWR_CLK1M_PD (0x0 << 8) +#define RT5682_PWR_CLK1M_PU (0x1 << 8) + +/* RC Clock Control (0x009f) */ +#define RT5682_POW_IRQ (0x1 << 15) +#define RT5682_POW_JDH (0x1 << 14) +#define RT5682_POW_JDL (0x1 << 13) +#define RT5682_POW_ANA (0x1 << 12) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5682_CLK_SRC_MCLK (0x0) +#define RT5682_CLK_SRC_PLL1 (0x1) +#define RT5682_CLK_SRC_PLL2 (0x2) +#define RT5682_CLK_SRC_SDW (0x3) +#define RT5682_CLK_SRC_RCCLK (0x4) +#define RT5682_I2S_PD_1 (0x0) +#define RT5682_I2S_PD_2 (0x1) +#define RT5682_I2S_PD_3 (0x2) +#define RT5682_I2S_PD_4 (0x3) +#define RT5682_I2S_PD_6 (0x4) +#define RT5682_I2S_PD_8 (0x5) +#define RT5682_I2S_PD_12 (0x6) +#define RT5682_I2S_PD_16 (0x7) +#define RT5682_I2S_PD_24 (0x8) +#define RT5682_I2S_PD_32 (0x9) +#define RT5682_I2S_PD_48 (0xa) +#define RT5682_I2S2_SRC_MASK (0x3 << 4) +#define RT5682_I2S2_SRC_SFT 4 +#define RT5682_I2S2_M_PD_MASK (0xf << 0) +#define RT5682_I2S2_M_PD_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5682_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5682_JD1_PULSE_EN_SFT 10 +#define RT5682_JD1_PULSE_DIS (0x0 << 10) +#define RT5682_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5682_JD1_EN_MASK (0x1 << 15) +#define RT5682_JD1_EN_SFT 15 +#define RT5682_JD1_DIS (0x0 << 15) +#define RT5682_JD1_EN (0x1 << 15) +#define RT5682_JD1_POL_MASK (0x1 << 13) +#define RT5682_JD1_POL_NOR (0x0 << 13) +#define RT5682_JD1_POL_INV (0x1 << 13) + +/* IRQ Control 3 (0x00b8) */ +#define RT5682_IL_IRQ_MASK (0x1 << 7) +#define RT5682_IL_IRQ_DIS (0x0 << 7) +#define RT5682_IL_IRQ_EN (0x1 << 7) + +/* GPIO Control 1 (0x00c0) */ +#define RT5682_GP1_PIN_MASK (0x3 << 14) +#define RT5682_GP1_PIN_SFT 14 +#define RT5682_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5682_GP1_PIN_IRQ (0x1 << 14) +#define RT5682_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5682_GP2_PIN_MASK (0x3 << 12) +#define RT5682_GP2_PIN_SFT 12 +#define RT5682_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5682_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5682_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5682_GP3_PIN_MASK (0x3 << 10) +#define RT5682_GP3_PIN_SFT 10 +#define RT5682_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5682_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5682_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5682_GP4_PIN_MASK (0x3 << 8) +#define RT5682_GP4_PIN_SFT 8 +#define RT5682_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5682_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5682_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5682_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5682_GP5_PIN_MASK (0x3 << 6) +#define RT5682_GP5_PIN_SFT 6 +#define RT5682_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5682_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5682_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5682_GP6_PIN_MASK (0x1 << 5) +#define RT5682_GP6_PIN_SFT 5 +#define RT5682_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5682_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5682_GP1_PF_MASK (0x1 << 15) +#define RT5682_GP1_PF_IN (0x0 << 15) +#define RT5682_GP1_PF_OUT (0x1 << 15) +#define RT5682_GP1_OUT_MASK (0x1 << 14) +#define RT5682_GP1_OUT_L (0x0 << 14) +#define RT5682_GP1_OUT_H (0x1 << 14) +#define RT5682_GP2_PF_MASK (0x1 << 13) +#define RT5682_GP2_PF_IN (0x0 << 13) +#define RT5682_GP2_PF_OUT (0x1 << 13) +#define RT5682_GP2_OUT_MASK (0x1 << 12) +#define RT5682_GP2_OUT_L (0x0 << 12) +#define RT5682_GP2_OUT_H (0x1 << 12) +#define RT5682_GP3_PF_MASK (0x1 << 11) +#define RT5682_GP3_PF_IN (0x0 << 11) +#define RT5682_GP3_PF_OUT (0x1 << 11) +#define RT5682_GP3_OUT_MASK (0x1 << 10) +#define RT5682_GP3_OUT_L (0x0 << 10) +#define RT5682_GP3_OUT_H (0x1 << 10) +#define RT5682_GP4_PF_MASK (0x1 << 9) +#define RT5682_GP4_PF_IN (0x0 << 9) +#define RT5682_GP4_PF_OUT (0x1 << 9) +#define RT5682_GP4_OUT_MASK (0x1 << 8) +#define RT5682_GP4_OUT_L (0x0 << 8) +#define RT5682_GP4_OUT_H (0x1 << 8) +#define RT5682_GP5_PF_MASK (0x1 << 7) +#define RT5682_GP5_PF_IN (0x0 << 7) +#define RT5682_GP5_PF_OUT (0x1 << 7) +#define RT5682_GP5_OUT_MASK (0x1 << 6) +#define RT5682_GP5_OUT_L (0x0 << 6) +#define RT5682_GP5_OUT_H (0x1 << 6) +#define RT5682_GP6_PF_MASK (0x1 << 5) +#define RT5682_GP6_PF_IN (0x0 << 5) +#define RT5682_GP6_PF_OUT (0x1 << 5) +#define RT5682_GP6_OUT_MASK (0x1 << 4) +#define RT5682_GP6_OUT_L (0x0 << 4) +#define RT5682_GP6_OUT_H (0x1 << 4) + + +/* GPIO Status (0x00c2) */ +#define RT5682_GP6_STA (0x1 << 6) +#define RT5682_GP5_STA (0x1 << 5) +#define RT5682_GP4_STA (0x1 << 4) +#define RT5682_GP3_STA (0x1 << 3) +#define RT5682_GP2_STA (0x1 << 2) +#define RT5682_GP1_STA (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5682_SV_MASK (0x1 << 15) +#define RT5682_SV_SFT 15 +#define RT5682_SV_DIS (0x0 << 15) +#define RT5682_SV_EN (0x1 << 15) +#define RT5682_ZCD_MASK (0x1 << 10) +#define RT5682_ZCD_SFT 10 +#define RT5682_ZCD_PD (0x0 << 10) +#define RT5682_ZCD_PU (0x1 << 10) +#define RT5682_SV_DLY_MASK (0xf) +#define RT5682_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5682_ZCD_BST1_CBJ_MASK (0x1 << 7) +#define RT5682_ZCD_BST1_CBJ_SFT 7 +#define RT5682_ZCD_BST1_CBJ_DIS (0x0 << 7) +#define RT5682_ZCD_BST1_CBJ_EN (0x1 << 7) +#define RT5682_ZCD_RECMIX_MASK (0x1) +#define RT5682_ZCD_RECMIX_SFT 0 +#define RT5682_ZCD_RECMIX_DIS (0x0) +#define RT5682_ZCD_RECMIX_EN (0x1) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5682_4BTN_IL_MASK (0x1 << 15) +#define RT5682_4BTN_IL_EN (0x1 << 15) +#define RT5682_4BTN_IL_DIS (0x0 << 15) +#define RT5682_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5682_4BTN_IL_NOR (0x1 << 14) +#define RT5682_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control (0x00f0) */ +#define RT5682_JDH_RS_MASK (0x1 << 4) +#define RT5682_JDH_NO_PLUG (0x1 << 4) +#define RT5682_JDH_PLUG (0x0 << 4) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5682_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5682_CKXEN_DAC1_SFT 13 +#define RT5682_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5682_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5682_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5682_CKXEN_ADC1_SFT 13 +#define RT5682_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5682_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5682_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5682_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5682_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5682_AD2DA_LB_MASK (0x1 << 10) +#define RT5682_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5682_NG2_EN_MASK (0x1 << 15) +#define RT5682_NG2_EN (0x1 << 15) +#define RT5682_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5682_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5682_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5682_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5682_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5682_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5682_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5682_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5682_SAR_BUTDET_RST (0x0 << 13) +#define RT5682_SAR_POW_MASK (0x1 << 12) +#define RT5682_SAR_POW_EN (0x1 << 12) +#define RT5682_SAR_POW_DIS (0x0 << 12) +#define RT5682_SAR_RST_MASK (0x1 << 11) +#define RT5682_SAR_RST_NORMAL (0x1 << 11) +#define RT5682_SAR_RST (0x0 << 11) +#define RT5682_SAR_BYPASS_MASK (0x1 << 10) +#define RT5682_SAR_BYPASS_EN (0x1 << 10) +#define RT5682_SAR_BYPASS_DIS (0x0 << 10) +#define RT5682_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5682_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5682_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5682_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5682_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5682_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5682_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5682_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5682_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5682_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5682_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5682_SAR_SOUR_MASK (0x3f) +#define RT5682_SAR_SOUR_BTN (0x3f) +#define RT5682_SAR_SOUR_TYPE (0x0) + + +/* System Clock Source */ +enum { + RT5682_SCLK_S_MCLK, + RT5682_SCLK_S_PLL1, + RT5682_SCLK_S_PLL2, + RT5682_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5682_PLL1_S_MCLK, + RT5682_PLL1_S_BCLK1, + RT5682_PLL1_S_RCCLK, +}; + +enum { + RT5682_AIF1, + RT5682_AIF2, + RT5682_AIFS +}; + +/* filter mask */ +enum { + RT5682_DA_STEREO1_FILTER = 0x1, + RT5682_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5682_CLK_SEL_SYS, + RT5682_CLK_SEL_I2S1_ASRC, + RT5682_CLK_SEL_I2S2_ASRC, +}; + +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5682_H__ */ diff --git a/sound/soc/codecs/dio2125.c b/sound/soc/codecs/simple-amplifier.c index 09451cd44f9b..85524acf3e9c 100644 --- a/sound/soc/codecs/dio2125.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -21,9 +21,9 @@ #include <linux/module.h> #include <sound/soc.h> -#define DRV_NAME "dio2125" +#define DRV_NAME "simple-amplifier" -struct dio2125 { +struct simple_amp { struct gpio_desc *gpiod_enable; }; @@ -31,7 +31,7 @@ static int drv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event) { struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); - struct dio2125 *priv = snd_soc_component_get_drvdata(c); + struct simple_amp *priv = snd_soc_component_get_drvdata(c); int val; switch (event) { @@ -51,7 +51,7 @@ static int drv_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = { +static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = { SND_SOC_DAPM_INPUT("INL"), SND_SOC_DAPM_INPUT("INR"), SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event, @@ -60,24 +60,24 @@ static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("OUTR"), }; -static const struct snd_soc_dapm_route dio2125_dapm_routes[] = { +static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = { { "DRV", NULL, "INL" }, { "DRV", NULL, "INR" }, { "OUTL", NULL, "DRV" }, { "OUTR", NULL, "DRV" }, }; -static const struct snd_soc_component_driver dio2125_component_driver = { - .dapm_widgets = dio2125_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(dio2125_dapm_widgets), - .dapm_routes = dio2125_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(dio2125_dapm_routes), +static const struct snd_soc_component_driver simple_amp_component_driver = { + .dapm_widgets = simple_amp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets), + .dapm_routes = simple_amp_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes), }; -static int dio2125_probe(struct platform_device *pdev) +static int simple_amp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct dio2125 *priv; + struct simple_amp *priv; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -93,28 +93,30 @@ static int dio2125_probe(struct platform_device *pdev) return err; } - return devm_snd_soc_register_component(dev, &dio2125_component_driver, + return devm_snd_soc_register_component(dev, + &simple_amp_component_driver, NULL, 0); } #ifdef CONFIG_OF -static const struct of_device_id dio2125_ids[] = { +static const struct of_device_id simple_amp_ids[] = { { .compatible = "dioo,dio2125", }, + { .compatible = "simple-audio-amplifier", }, { } }; -MODULE_DEVICE_TABLE(of, dio2125_ids); +MODULE_DEVICE_TABLE(of, simple_amp_ids); #endif -static struct platform_driver dio2125_driver = { +static struct platform_driver simple_amp_driver = { .driver = { .name = DRV_NAME, - .of_match_table = of_match_ptr(dio2125_ids), + .of_match_table = of_match_ptr(simple_amp_ids), }, - .probe = dio2125_probe, + .probe = simple_amp_probe, }; -module_platform_driver(dio2125_driver); +module_platform_driver(simple_amp_driver); -MODULE_DESCRIPTION("ASoC DIO2125 output driver"); +MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver"); MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 52f34c94ec25..ca2dfe12344e 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -7,6 +7,9 @@ * TAS5721 support: * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com> * + * TAS5707 support: + * Copyright (C) 2018 Jerome Brunet, Baylibre SAS <jbrunet@baylibre.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -444,6 +447,111 @@ static const struct tas571x_chip tas5711_chip = { .vol_reg_size = 1, }; +static const struct regmap_range tas5707_volatile_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), + regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), + regmap_reg_range(TAS5707_CH1_BQ0_REG, TAS5707_CH2_BQ6_REG), +}; + +static const struct regmap_access_table tas5707_volatile_regs = { + .yes_ranges = tas5707_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(tas5707_volatile_regs_range), + +}; + +static const DECLARE_TLV_DB_SCALE(tas5707_volume_tlv, -7900, 50, 1); + +static const char * const tas5707_volume_slew_step_txt[] = { + "256", "512", "1024", "2048", +}; + +static const unsigned int tas5707_volume_slew_step_values[] = { + 3, 0, 1, 2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(tas5707_volume_slew_step_enum, + TAS571X_VOL_CFG_REG, 0, 0x3, + tas5707_volume_slew_step_txt, + tas5707_volume_slew_step_values); + +static const struct snd_kcontrol_new tas5707_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5707_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5707_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), + + SOC_ENUM("Slew Rate Steps", tas5707_volume_slew_step_enum), + + BIQUAD_COEFS("CH1 - Biquad 0", TAS5707_CH1_BQ0_REG), + BIQUAD_COEFS("CH1 - Biquad 1", TAS5707_CH1_BQ1_REG), + BIQUAD_COEFS("CH1 - Biquad 2", TAS5707_CH1_BQ2_REG), + BIQUAD_COEFS("CH1 - Biquad 3", TAS5707_CH1_BQ3_REG), + BIQUAD_COEFS("CH1 - Biquad 4", TAS5707_CH1_BQ4_REG), + BIQUAD_COEFS("CH1 - Biquad 5", TAS5707_CH1_BQ5_REG), + BIQUAD_COEFS("CH1 - Biquad 6", TAS5707_CH1_BQ6_REG), + + BIQUAD_COEFS("CH2 - Biquad 0", TAS5707_CH2_BQ0_REG), + BIQUAD_COEFS("CH2 - Biquad 1", TAS5707_CH2_BQ1_REG), + BIQUAD_COEFS("CH2 - Biquad 2", TAS5707_CH2_BQ2_REG), + BIQUAD_COEFS("CH2 - Biquad 3", TAS5707_CH2_BQ3_REG), + BIQUAD_COEFS("CH2 - Biquad 4", TAS5707_CH2_BQ4_REG), + BIQUAD_COEFS("CH2 - Biquad 5", TAS5707_CH2_BQ5_REG), + BIQUAD_COEFS("CH2 - Biquad 6", TAS5707_CH2_BQ6_REG), +}; + +static const struct reg_default tas5707_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x70}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0xff}, + {TAS571X_CH1_VOL_REG, 0x30}, + {TAS571X_CH2_VOL_REG, 0x30}, + {TAS571X_VOL_CFG_REG, 0x91}, + {TAS571X_MODULATION_LIMIT_REG, 0x02}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_START_STOP_PERIOD_REG, 0x0f}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x02}, + {TAS571X_INPUT_MUX_REG, 0x17772}, + {TAS571X_PWM_MUX_REG, 0x1021345}, +}; + +static const struct regmap_config tas5707_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5707_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5707_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas5707_volatile_regs, +}; + +static const struct tas571x_chip tas5707_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5707_controls, + .num_controls = ARRAY_SIZE(tas5707_controls), + .regmap_config = &tas5707_regmap_config, + .vol_reg_size = 1, +}; + static const char *const tas5717_supply_names[] = { "AVDD", "DVDD", @@ -775,6 +883,7 @@ static int tas571x_i2c_remove(struct i2c_client *client) } static const struct of_device_id tas571x_of_match[] = { + { .compatible = "ti,tas5707", .data = &tas5707_chip, }, { .compatible = "ti,tas5711", .data = &tas5711_chip, }, { .compatible = "ti,tas5717", .data = &tas5717_chip, }, { .compatible = "ti,tas5719", .data = &tas5717_chip, }, @@ -784,6 +893,7 @@ static const struct of_device_id tas571x_of_match[] = { MODULE_DEVICE_TABLE(of, tas571x_of_match); static const struct i2c_device_id tas571x_i2c_id[] = { + { "tas5707", (kernel_ulong_t) &tas5707_chip }, { "tas5711", (kernel_ulong_t) &tas5711_chip }, { "tas5717", (kernel_ulong_t) &tas5717_chip }, { "tas5719", (kernel_ulong_t) &tas5717_chip }, diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h index c45677bc26ad..bd23e89cfe79 100644 --- a/sound/soc/codecs/tas571x.h +++ b/sound/soc/codecs/tas571x.h @@ -53,6 +53,22 @@ #define TAS571X_PWM_MUX_REG 0x25 /* 20-byte biquad registers */ +#define TAS5707_CH1_BQ0_REG 0x29 +#define TAS5707_CH1_BQ1_REG 0x2a +#define TAS5707_CH1_BQ2_REG 0x2b +#define TAS5707_CH1_BQ3_REG 0x2c +#define TAS5707_CH1_BQ4_REG 0x2d +#define TAS5707_CH1_BQ5_REG 0x2e +#define TAS5707_CH1_BQ6_REG 0x2f + +#define TAS5707_CH2_BQ0_REG 0x30 +#define TAS5707_CH2_BQ1_REG 0x31 +#define TAS5707_CH2_BQ2_REG 0x32 +#define TAS5707_CH2_BQ3_REG 0x33 +#define TAS5707_CH2_BQ4_REG 0x34 +#define TAS5707_CH2_BQ5_REG 0x35 +#define TAS5707_CH2_BQ6_REG 0x36 + #define TAS5717_CH1_BQ0_REG 0x26 #define TAS5717_CH1_BQ1_REG 0x27 #define TAS5717_CH1_BQ2_REG 0x28 diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index 225c210ac38f..7f3b79c5a563 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -142,9 +142,9 @@ struct tda7419_vol_control { static inline bool tda7419_vol_is_stereo(struct tda7419_vol_control *tvc) { if (tvc->reg == tvc->rreg) - return 0; + return false; - return 1; + return true; } static int tda7419_vol_info(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index d18ff17719cc..7396a6e5277e 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -625,25 +625,34 @@ static int bytes_info_ext(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { /* Volumes */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", R_HPVOLL, R_HPVOLR, + SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR, FB_HPVOLL, 0x7F, 0, hpvol_scale), - SOC_DOUBLE_R_TLV("Speaker Playback Volume", R_SPKVOLL, R_SPKVOLR, + SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR, FB_SPKVOLL, 0x7F, 0, spkvol_scale), - SOC_DOUBLE_R_TLV("Master Playback Volume", R_DACVOLL, R_DACVOLR, + SOC_DOUBLE_R_TLV("Master Volume", R_DACVOLL, R_DACVOLR, FB_DACVOLL, 0xFF, 0, dacvol_scale), - SOC_DOUBLE_R_TLV("PCM Capture Volume", R_ADCVOLL, R_ADCVOLR, + SOC_DOUBLE_R_TLV("PCM Volume", R_ADCVOLL, R_ADCVOLR, FB_ADCVOLL, 0xFF, 0, adcvol_scale), - SOC_DOUBLE_R_TLV("Master Capture Volume", R_INVOLL, R_INVOLR, + SOC_DOUBLE_R_TLV("Input Volume", R_INVOLL, R_INVOLR, FB_INVOLL, 0x3F, 0, invol_scale), /* INSEL */ - SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", R_INSELL, R_INSELR, + SOC_DOUBLE_R_TLV("Mic Boost Volume", R_INSELL, R_INSELR, FB_INSELL_MICBSTL, FV_INSELL_MICBSTL_30DB, 0, mic_boost_scale), /* Input Channel Map */ SOC_ENUM("Input Channel Map", ch_map_select_enum), + /* Mic Bias */ + SOC_SINGLE("Mic Bias Boost Switch", 0x71, 0x07, 1, 0), + + /* Headphone Auto Switching */ + SOC_SINGLE("Headphone Auto Switching Switch", + R_CTL, FB_CTL_HPSWEN, 1, 0), + SOC_SINGLE("Headphone Detect Polarity Toggle Switch", + R_CTL, FB_CTL_HPSWPOL, 1, 0), + /* Coefficient Ram */ COEFF_RAM_CTL("Cascade1L BiQuad1", BIQUAD_SIZE, 0x00), COEFF_RAM_CTL("Cascade1L BiQuad2", BIQUAD_SIZE, 0x05), @@ -733,9 +742,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { R_CLECTL, FB_CLECTL_LIMIT_EN, 1, 0), SOC_SINGLE("Comp Switch", R_CLECTL, FB_CLECTL_COMP_EN, 1, 0), - SOC_SINGLE_TLV("CLE Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("CLE Make-Up Gain Volume", R_MUGAIN, FB_MUGAIN_CLEMUG, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("Comp Thresh Playback Volume", + SOC_SINGLE_TLV("Comp Thresh Volume", R_COMPTH, FB_COMPTH, 0xff, 0, compth_scale), SOC_ENUM("Comp Ratio", compressor_ratio_enum), SND_SOC_BYTES("Comp Atk Time", R_CATKTCL, 2), @@ -766,9 +775,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { SOC_SINGLE("MBC1 Phase Invert Switch", R_DACMBCMUG1, FB_DACMBCMUG1_PHASE, 1, 0), - SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Volume", R_DACMBCMUG1, FB_DACMBCMUG1_MUGAIN, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Playback Volume", + SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Volume", R_DACMBCTHR1, FB_DACMBCTHR1_THRESH, 0xff, 0, compth_scale), SOC_ENUM("DAC MBC1 Comp Ratio", dac_mbc1_compressor_ratio_enum), @@ -778,9 +787,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { SOC_SINGLE("MBC2 Phase Invert Switch", R_DACMBCMUG2, FB_DACMBCMUG2_PHASE, 1, 0), - SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Volume", R_DACMBCMUG2, FB_DACMBCMUG2_MUGAIN, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Playback Volume", + SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Volume", R_DACMBCTHR2, FB_DACMBCTHR2_THRESH, 0xff, 0, compth_scale), SOC_ENUM("DAC MBC2 Comp Ratio", dac_mbc2_compressor_ratio_enum), @@ -790,9 +799,9 @@ static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { SOC_SINGLE("MBC3 Phase Invert Switch", R_DACMBCMUG3, FB_DACMBCMUG3_PHASE, 1, 0), - SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Playback Volume", + SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Volume", R_DACMBCMUG3, FB_DACMBCMUG3_MUGAIN, 0x1f, 0, mugain_scale), - SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Playback Volume", + SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Volume", R_DACMBCTHR3, FB_DACMBCTHR3_THRESH, 0xff, 0, compth_scale), SOC_ENUM("DAC MBC3 Comp Ratio", dac_mbc3_compressor_ratio_enum), diff --git a/sound/soc/codecs/tscs42xx.h b/sound/soc/codecs/tscs42xx.h index 814c8f3c4a68..6b3a21081635 100644 --- a/sound/soc/codecs/tscs42xx.h +++ b/sound/soc/codecs/tscs42xx.h @@ -34,6 +34,7 @@ enum { #define R_DACSR 0x19 #define R_PWRM1 0x1A #define R_PWRM2 0x1B +#define R_CTL 0x1C #define R_CONFIG0 0x1F #define R_CONFIG1 0x20 #define R_DMICCTL 0x24 @@ -1110,6 +1111,13 @@ enum { #define RV_PWRM2_VREF_DISABLE \ RV(FV_PWRM2_VREF_DISABLE, FB_PWRM2_VREF) +/****************************** + * R_CTL (0x1C) * + ******************************/ + +/* Fiel Offsets */ +#define FB_CTL_HPSWEN 7 +#define FB_CTL_HPSWPOL 6 /****************************** * R_CONFIG0 (0x1F) * diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index bfd1abd72253..94675da514c8 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -148,7 +148,7 @@ static bool twl6040_can_write_to_chip(struct snd_soc_component *component, case TWL6040_REG_HFRCTL: return priv->dl2_unmuted; default: - return 1; + return true; } } diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 3663b9fd4d65..deff65161504 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1180,6 +1180,9 @@ SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L, SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT, WM2200_SPK1R_MUTE_SHIFT, 1, 1), SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), }; WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE); @@ -1553,15 +1556,10 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = { static int wm2200_probe(struct snd_soc_component *component) { struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); - int ret; wm2200->component = component; - ret = snd_soc_add_component_controls(component, wm_adsp_fw_controls, 2); - if (ret != 0) - return ret; - - return ret; + return 0; } static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c index e239f4bf2460..9e987cf07450 100644 --- a/sound/soc/codecs/wm5100-tables.c +++ b/sound/soc/codecs/wm5100-tables.c @@ -30,7 +30,7 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg) case WM5100_OUTPUT_STATUS_2: case WM5100_INPUT_ENABLES_STATUS: case WM5100_MIC_DETECT_3: - return 1; + return true; default: if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || @@ -41,9 +41,9 @@ bool wm5100_volatile_register(struct device *dev, unsigned int reg) (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) - return 1; + return true; else - return 0; + return false; } } @@ -798,7 +798,7 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg) case WM5100_DSP3_CONTROL_28: case WM5100_DSP3_CONTROL_29: case WM5100_DSP3_CONTROL_30: - return 1; + return true; default: if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || @@ -809,9 +809,9 @@ bool wm5100_readable_register(struct device *dev, unsigned int reg) (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) - return 1; + return true; else - return 0; + return false; } } diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 1ac83388d1b8..7e817e1877c2 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -985,6 +985,8 @@ ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -2094,6 +2096,12 @@ static int wm5102_probe(struct platform_device *pdev) return ret; } + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -2117,6 +2125,7 @@ static int wm5102_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); return ret; @@ -2133,6 +2142,7 @@ static int wm5102_remove(struct platform_device *pdev) arizona_free_spk_irqs(arizona); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); return 0; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index fb9835dcd836..b0789a03d699 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -927,6 +927,11 @@ ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +WM_ADSP_FW_CONTROL("DSP4", 3), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -2455,6 +2460,12 @@ static int wm5110_probe(struct platform_device *pdev) return ret; } + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -2478,6 +2489,7 @@ static int wm5110_probe(struct platform_device *pdev) err_spk_irqs: arizona_free_spk_irqs(arizona); err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); return ret; @@ -2496,6 +2508,7 @@ static int wm5110_remove(struct platform_device *pdev) arizona_free_spk_irqs(arizona); + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); return 0; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 7b8b6ef2f632..6cb3c153ba19 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -251,10 +251,10 @@ static bool wm8903_volatile_register(struct device *dev, unsigned int reg) case WM8903_DC_SERVO_READBACK_2: case WM8903_DC_SERVO_READBACK_3: case WM8903_DC_SERVO_READBACK_4: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9037a35b931d..1965635ec07c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1455,6 +1455,7 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= 0x3 | WM8904_AIF_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x3; break; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index ba44e3d6c1e0..cd204f79647d 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -686,6 +686,7 @@ static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif |= WM8955_LRP; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif |= 0x3; break; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index c30f5aa392c6..8dc1f3d6a988 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -839,6 +839,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, iface |= 0x000c; break; } + /* fall through */ default: dev_err(component->dev, "unsupported width %d\n", params_width(params)); diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index f70f563d59f3..68b4cadc308f 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -653,6 +653,7 @@ static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_B: aif |= WM8961_LRP; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif |= 3; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index a11e9d6bf950..efd8910b1ff7 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2649,6 +2649,7 @@ static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif0 |= WM8962_LRCLK_INV | 3; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif0 |= 3; diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 62200117444b..6e52c6a8bab3 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -522,7 +522,7 @@ static inline int get_coeff(int mclk, int rate) /* The set of rates we can generate from the above for each SYSCLK */ static const unsigned int rates_12288[] = { - 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, + 8000, 12000, 16000, 24000, 32000, 48000, 96000, }; static const struct snd_pcm_hw_constraint_list constraints_12288 = { @@ -540,7 +540,7 @@ static const struct snd_pcm_hw_constraint_list constraints_112896 = { }; static const unsigned int rates_12[] = { - 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000, + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 41100, 48000, 48000, 88235, 96000, }; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 411b9eee88c2..457bc437ce54 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -40,9 +40,9 @@ static bool wm8990_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8990_RESET: - return 1; + return true; default: - return 0; + return false; } } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 7fdfdf3f6e67..14f1b0c0d286 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2432,6 +2432,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_2, WM8994_OPCLK_ENA, 0); } + break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 60e227832331..68c99fe37097 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1465,6 +1465,7 @@ static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif |= WM8995_AIF1_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif |= (0x3 << WM8995_AIF1_FMT_SHIFT); break; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index d9d206046f8c..91711f8958c5 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1498,9 +1498,9 @@ static bool wm8996_readable_register(struct device *dev, unsigned int reg) case WM8996_RIGHT_PDM_SPEAKER: case WM8996_PDM_SPEAKER_MUTE_SEQUENCE: case WM8996_PDM_SPEAKER_VOLUME: - return 1; + return true; default: - return 0; + return false; } } @@ -1522,9 +1522,9 @@ static bool wm8996_volatile_register(struct device *dev, unsigned int reg) case WM8996_MIC_DETECT_3: case WM8996_HEADPHONE_DETECT_1: case WM8996_HEADPHONE_DETECT_2: - return 1; + return true; default: - return 0; + return false; } } @@ -1858,6 +1858,7 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, case 24576000: ratediv = WM8996_SYSCLK_DIV; wm8996->sysclk /= 2; + /* fall through */ case 11289600: case 12288000: snd_soc_component_update_bits(component, WM8996_AIF_RATE, diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 5a0ea7b3c149..399255d1f78a 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -933,6 +933,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif2 |= WM9081_AIF_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif2 |= 0x3; break; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 2fcdd84021a5..f61656070225 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include <linux/ctype.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -35,15 +36,15 @@ #include "wm_adsp.h" #define adsp_crit(_dsp, fmt, ...) \ - dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_err(_dsp, fmt, ...) \ - dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_warn(_dsp, fmt, ...) \ - dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_info(_dsp, fmt, ...) \ - dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define adsp_dbg(_dsp, fmt, ...) \ - dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) + dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) #define ADSP1_CONTROL_1 0x00 #define ADSP1_CONTROL_2 0x02 @@ -418,7 +419,7 @@ static const struct wm_adsp_fw_caps ctrl_caps[] = { { .id = SND_AUDIOCODEC_BESPOKE, .desc = { - .max_ch = 1, + .max_ch = 8, .sample_rates = { 16000 }, .num_sample_rates = 1, .formats = SNDRV_PCM_FMTBIT_S16_LE, @@ -608,7 +609,6 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, struct snd_soc_component *component) { struct dentry *root = NULL; - char *root_name; int i; if (!component->debugfs_root) { @@ -616,13 +616,7 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, goto err; } - root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!root_name) - goto err; - - snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); - root = debugfs_create_dir(root_name, component->debugfs_root); - kfree(root_name); + root = debugfs_create_dir(dsp->name, component->debugfs_root); if (!root) goto err; @@ -684,8 +678,8 @@ static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) } #endif -static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -695,9 +689,10 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, return 0; } +EXPORT_SYMBOL_GPL(wm_adsp_fw_get); -static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -721,8 +716,9 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, return ret; } +EXPORT_SYMBOL_GPL(wm_adsp_fw_put); -static const struct soc_enum wm_adsp_fw_enum[] = { +const struct soc_enum wm_adsp_fw_enum[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), @@ -731,24 +727,7 @@ static const struct soc_enum wm_adsp_fw_enum[] = { SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), }; - -const struct snd_kcontrol_new wm_adsp_fw_controls[] = { - SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6], - wm_adsp_fw_get, wm_adsp_fw_put), -}; -EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); +EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -1330,12 +1309,12 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, switch (dsp->fw_ver) { case 0: case 1: - snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", - dsp->num, region_name, alg_region->alg); + snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", + dsp->name, region_name, alg_region->alg); break; default: ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "DSP%d%c %.12s %x", dsp->num, *region_name, + "%s%c %.12s %x", dsp->name, *region_name, wm_adsp_fw_text[dsp->fw], alg_region->alg); /* Truncate the subname from the start if it is too long */ @@ -1343,6 +1322,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; int skip = 0; + if (dsp->component->name_prefix) + avail -= strlen(dsp->component->name_prefix) + 1; + if (subname_len > avail) skip = subname_len - avail; @@ -1604,6 +1586,15 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp, if (ret) return -EINVAL; break; + case WMFW_CTL_TYPE_HOST_BUFFER: + ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, + WMFW_CTL_FLAG_SYS | + WMFW_CTL_FLAG_VOLATILE | + WMFW_CTL_FLAG_READABLE, + 0); + if (ret) + return -EINVAL; + break; default: adsp_err(dsp, "Unknown control type: %d\n", coeff_blk.ctl_type); @@ -1651,7 +1642,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; @@ -1871,9 +1862,11 @@ static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, } static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, + const struct wm_adsp_region *mem, unsigned int pos, unsigned int len) { void *alg; + unsigned int reg; int ret; __be32 val; @@ -1888,7 +1881,9 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, } /* Read the terminator first to validate the length */ - ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); + reg = wm_adsp_region_to_reg(mem, pos + len); + + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); if (ret != 0) { adsp_err(dsp, "Failed to read algorithm list end: %d\n", ret); @@ -1897,13 +1892,18 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, if (be32_to_cpu(val) != 0xbedead) adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", - pos + len, be32_to_cpu(val)); + reg, be32_to_cpu(val)); + + /* Convert length from DSP words to bytes */ + len *= sizeof(u32); - alg = kcalloc(len, 2, GFP_KERNEL | GFP_DMA); + alg = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!alg) return ERR_PTR(-ENOMEM); - ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); + reg = wm_adsp_region_to_reg(mem, pos); + + ret = regmap_raw_read(dsp->regmap, reg, alg, len); if (ret != 0) { adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); kfree(alg); @@ -2002,10 +2002,11 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp) if (IS_ERR(alg_region)) return PTR_ERR(alg_region); - pos = sizeof(adsp1_id) / 2; - len = (sizeof(*adsp1_alg) * n_algs) / 2; + /* Calculate offset and length in DSP words */ + pos = sizeof(adsp1_id) / sizeof(u32); + len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); - adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); if (IS_ERR(adsp1_alg)) return PTR_ERR(adsp1_alg); @@ -2113,10 +2114,11 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) if (IS_ERR(alg_region)) return PTR_ERR(alg_region); - pos = sizeof(adsp2_id) / 2; - len = (sizeof(*adsp2_alg) * n_algs) / 2; + /* Calculate offset and length in DSP words */ + pos = sizeof(adsp2_id) / sizeof(u32); + len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); - adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); if (IS_ERR(adsp2_alg)) return PTR_ERR(adsp2_alg); @@ -2218,7 +2220,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; @@ -2390,8 +2392,38 @@ out: return ret; } +static int wm_adsp_create_name(struct wm_adsp *dsp) +{ + char *p; + + if (!dsp->name) { + dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", + dsp->num); + if (!dsp->name) + return -ENOMEM; + } + + if (!dsp->fwf_name) { + p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); + if (!p) + return -ENOMEM; + + dsp->fwf_name = p; + for (; *p != 0; ++p) + *p = tolower(*p); + } + + return 0; +} + int wm_adsp1_init(struct wm_adsp *dsp) { + int ret; + + ret = wm_adsp_create_name(dsp); + if (ret) + return ret; + INIT_LIST_HEAD(&dsp->alg_regions); mutex_init(&dsp->pwr_lock); @@ -2642,7 +2674,10 @@ int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct wm_adsp *dsp = &dsps[mc->shift - 1]; ucontrol->value.integer.value[0] = dsp->preloaded; @@ -2654,13 +2689,14 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct wm_adsp *dsp = &dsps[mc->shift - 1]; char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift); + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); dsp->preloaded = ucontrol->value.integer.value[0]; @@ -2671,6 +2707,8 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, snd_soc_dapm_sync(dapm); + flush_work(&dsp->boot_work); + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); @@ -2853,17 +2891,14 @@ int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *comp { char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num); - + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); snd_soc_component_disable_pin(component, preload); wm_adsp2_init_debugfs(dsp, component); dsp->component = component; - return snd_soc_add_component_controls(component, - &wm_adsp_fw_controls[dsp->num - 1], - 1); + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); @@ -2879,6 +2914,10 @@ int wm_adsp2_init(struct wm_adsp *dsp) { int ret; + ret = wm_adsp_create_name(dsp); + if (ret) + return ret; + switch (dsp->rev) { case 0: /* @@ -3186,7 +3225,7 @@ static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, buf->host_buf_ptr + field_offset, data); } -static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) { struct wm_adsp_alg_region *alg_region; struct wm_adsp *dsp = buf->dsp; @@ -3225,6 +3264,61 @@ static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) return 0; } +static struct wm_coeff_ctl * +wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf) +{ + struct wm_adsp *dsp = buf->dsp; + struct wm_coeff_ctl *ctl; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) + continue; + + if (!ctl->enabled) + continue; + + return ctl; + } + + return NULL; +} + +static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +{ + struct wm_adsp *dsp = buf->dsp; + struct wm_coeff_ctl *ctl; + unsigned int reg; + u32 val; + int i, ret; + + ctl = wm_adsp_find_host_buffer_ctrl(buf); + if (!ctl) + return wm_adsp_legacy_host_buf_addr(buf); + + ret = wm_coeff_base_reg(ctl, ®); + if (ret) + return ret; + + for (i = 0; i < 5; ++i) { + ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + if (ret < 0) + return ret; + + if (val) + break; + + usleep_range(1000, 2000); + } + + if (!val) + return -EIO; + + buf->host_buf_ptr = be32_to_cpu(val); + adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); + + return 0; +} + static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) { const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index bc6d359f0533..4b8778b0b06c 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -57,6 +57,8 @@ struct wm_adsp_compr_buf; struct wm_adsp { const char *part; + const char *name; + const char *fwf_name; int rev; int num; int type; @@ -121,7 +123,11 @@ struct wm_adsp { .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } -extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; +#define WM_ADSP_FW_CONTROL(dspname, num) \ + SOC_ENUM_EXT(dspname " Firmware", wm_adsp_fw_enum[num], \ + wm_adsp_fw_get, wm_adsp_fw_put) + +extern const struct soc_enum wm_adsp_fw_enum[]; int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp); @@ -144,6 +150,10 @@ int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream); int wm_adsp_compr_free(struct snd_compr_stream *stream); diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index ec78b9da020f..0c3f50acb8b1 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -29,6 +29,7 @@ /* Non-ALSA coefficient types start at 0x1000 */ #define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */ #define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */ +#define WMFW_CTL_TYPE_HOST_BUFFER 0x1002 /* host buffer pointer */ struct wmfw_header { char magic[4]; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 807040bb3921..a3206e65e5e5 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -340,6 +340,7 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, * rate is lowered. */ inv_fs = true; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: dev->mode = MOD_DSP_A; break; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 47c0c821d325..f70db8412c7c 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -320,12 +320,8 @@ static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) handled_mask |= XUNDRN; substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; - if (substream) { - snd_pcm_stream_lock_irq(substream); - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(substream); - } + if (substream) + snd_pcm_stop_xrun(substream); } if (!handled_mask) @@ -355,12 +351,8 @@ static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) handled_mask |= ROVRN; substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; - if (substream) { - snd_pcm_stream_lock_irq(substream); - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(substream); - } + if (substream) + snd_pcm_stop_xrun(substream); } if (!handled_mask) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 4a6750aa3637..44433b20435c 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -1,14 +1,10 @@ -/* - * Freescale Generic ASoC Sound Card driver with ASRC - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale Generic ASoC Sound Card driver with ASRC +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. +// +// Author: Nicolin Chen <nicoleotsuka@gmail.com> #include <linux/clk.h> #include <linux/i2c.h> @@ -199,7 +195,7 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_none(mask); - snd_mask_set(mask, (__force int)priv->asrc_format); + snd_mask_set_format(mask, priv->asrc_format); return 0; } diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index adfb8135d739..528e8b108422 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1,14 +1,10 @@ -/* - * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. +// +// Author: Nicolin Chen <nicoleotsuka@gmail.com> #include <linux/clk.h> #include <linux/delay.h> diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index d558dd5499a5..c60075112570 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * fsl_asrc.h - Freescale ASRC ALSA SoC header file * * Copyright (C) 2014 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _FSL_ASRC_H diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 565e16d8fe85..1033ac6631b0 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -1,14 +1,10 @@ -/* - * Freescale ASRC ALSA SoC Platform (DMA) driver - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * Author: Nicolin Chen <nicoleotsuka@gmail.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ASRC ALSA SoC Platform (DMA) driver +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. +// +// Author: Nicolin Chen <nicoleotsuka@gmail.com> #include <linux/dma-mapping.h> #include <linux/module.h> diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 8f43110373b8..c1d1d06783e5 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -249,6 +249,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, break; case ESAI_HCKT_EXTAL: ecr |= ESAI_ECR_ETI; + /* fall through */ case ESAI_HCKR_EXTAL: ecr |= ESAI_ECR_ERI; break; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 9b59d87b61bf..740b90df44bb 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1118,7 +1118,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) { for (txclk_df = 1; txclk_df <= 128; txclk_df++) { - rate_ideal = rate[index] * txclk_df * 64; + rate_ideal = rate[index] * txclk_df * 64ULL; if (round) rate_actual = clk_round_rate(clk, rate_ideal); else diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 7592b0406370..7f0fa4b52223 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -1,14 +1,10 @@ -/** - * Freescale ALSA SoC Machine driver utility - * - * Author: Timur Tabi <timur@freescale.com> - * - * Copyright 2010 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ALSA SoC Machine driver utility +// +// Author: Timur Tabi <timur@freescale.com> +// +// Copyright 2010 Freescale Semiconductor, Inc. #include <linux/module.h> #include <linux/of_address.h> diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index 1687b66ef18e..c5dc2a14b492 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -1,13 +1,10 @@ -/** +/* SPDX-License-Identifier: GPL-2.0 */ +/* * Freescale ALSA SoC Machine driver utility * * Author: Timur Tabi <timur@freescale.com> * * Copyright 2010 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _FSL_UTILS_H diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index b99e0b5e00e9..c29200cf755a 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -1,14 +1,7 @@ -/* - * Copyright 2012 Freescale Semiconductor, Inc. - * Copyright 2012 Linaro Ltd. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2012 Freescale Semiconductor, Inc. +// Copyright 2012 Linaro Ltd. #include <linux/module.h> #include <linux/of.h> diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index d93bacacbd5b..2094d2c8919f 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -1,15 +1,12 @@ -/* - * ASoC audio graph sound card support - * - * Copyright (C) 2016 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on ${LINUX}/sound/soc/generic/simple-card.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC audio graph sound card support +// +// Copyright (C) 2016 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// based on ${LINUX}/sound/soc/generic/simple-card.c + #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio.h> @@ -21,7 +18,6 @@ #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/string.h> -#include <sound/jack.h> #include <sound/simple_card_utils.h> struct graph_card_data { @@ -32,6 +28,8 @@ struct graph_card_data { unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; + struct asoc_simple_jack hp_jack; + struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct gpio_desc *pa_gpio; }; @@ -278,6 +276,22 @@ static int asoc_graph_get_dais_count(struct device *dev) return count; } +static int asoc_graph_soc_card_probe(struct snd_soc_card *card) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(card); + int ret; + + ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_mic(card, &priv->mic_jack, NULL); + if (ret < 0) + return ret; + + return 0; +} + static int asoc_graph_card_probe(struct platform_device *pdev) { struct graph_card_data *priv; @@ -319,6 +333,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) card->num_links = num; card->dapm_widgets = asoc_graph_card_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); + card->probe = asoc_graph_soc_card_probe; ret = asoc_graph_card_parse_of(priv); if (ret < 0) { diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index 095ef6426d42..92882e392d6c 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -1,17 +1,14 @@ -/* - * ASoC audio graph SCU sound card support - * - * Copyright (C) 2017 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on - * ${LINUX}/sound/soc/generic/simple-scu-card.c - * ${LINUX}/sound/soc/generic/audio-graph-card.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC audio graph SCU sound card support +// +// Copyright (C) 2017 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// based on +// ${LINUX}/sound/soc/generic/simple-scu-card.c +// ${LINUX}/sound/soc/generic/audio-graph-card.c + #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio.h> diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 3751a07de6aa..d3f3f0fec74c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1,16 +1,17 @@ -/* - * simple-card-utils.c - * - * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// simple-card-utils.c +// +// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/of_graph.h> +#include <sound/jack.h> #include <sound/simple_card_utils.h> void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, @@ -419,6 +420,61 @@ int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets); +int asoc_simple_card_init_jack(struct snd_soc_card *card, + struct asoc_simple_jack *sjack, + int is_hp, char *prefix) +{ + struct device *dev = card->dev; + enum of_gpio_flags flags; + char prop[128]; + char *pin_name; + char *gpio_name; + int mask; + int det; + + if (!prefix) + prefix = ""; + + sjack->gpio.gpio = -ENOENT; + + if (is_hp) { + snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix); + pin_name = "Headphones"; + gpio_name = "Headphone detection"; + mask = SND_JACK_HEADPHONE; + } else { + snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix); + pin_name = "Mic Jack"; + gpio_name = "Mic detection"; + mask = SND_JACK_MICROPHONE; + } + + det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags); + if (det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (gpio_is_valid(det)) { + sjack->pin.pin = pin_name; + sjack->pin.mask = mask; + + sjack->gpio.name = gpio_name; + sjack->gpio.report = mask; + sjack->gpio.gpio = det; + sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); + sjack->gpio.debounce_time = 150; + + snd_soc_card_jack_new(card, pin_name, mask, + &sjack->jack, + &sjack->pin, 1); + + snd_soc_jack_add_gpios(&sjack->jack, 1, + &sjack->gpio); + } + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_init_jack); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 8b374af86a6e..64bf3560c1d1 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -1,32 +1,20 @@ -/* - * ASoC simple sound card support - * - * Copyright (C) 2012 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC simple sound card support +// +// Copyright (C) 2012 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/clk.h> #include <linux/device.h> -#include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/string.h> -#include <sound/jack.h> #include <sound/simple_card.h> #include <sound/soc-dai.h> #include <sound/soc.h> -struct asoc_simple_jack { - struct snd_soc_jack jack; - struct snd_soc_jack_pin pin; - struct snd_soc_jack_gpio gpio; -}; - struct simple_card_data { struct snd_soc_card snd_card; struct simple_dai_props { @@ -49,61 +37,6 @@ struct simple_card_data { #define CELL "#sound-dai-cells" #define PREFIX "simple-audio-card," -#define asoc_simple_card_init_hp(card, sjack, prefix)\ - asoc_simple_card_init_jack(card, sjack, 1, prefix) -#define asoc_simple_card_init_mic(card, sjack, prefix)\ - asoc_simple_card_init_jack(card, sjack, 0, prefix) -static int asoc_simple_card_init_jack(struct snd_soc_card *card, - struct asoc_simple_jack *sjack, - int is_hp, char *prefix) -{ - struct device *dev = card->dev; - enum of_gpio_flags flags; - char prop[128]; - char *pin_name; - char *gpio_name; - int mask; - int det; - - sjack->gpio.gpio = -ENOENT; - - if (is_hp) { - snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix); - pin_name = "Headphones"; - gpio_name = "Headphone detection"; - mask = SND_JACK_HEADPHONE; - } else { - snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix); - pin_name = "Mic Jack"; - gpio_name = "Mic detection"; - mask = SND_JACK_MICROPHONE; - } - - det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags); - if (det == -EPROBE_DEFER) - return -EPROBE_DEFER; - - if (gpio_is_valid(det)) { - sjack->pin.pin = pin_name; - sjack->pin.mask = mask; - - sjack->gpio.name = gpio_name; - sjack->gpio.report = mask; - sjack->gpio.gpio = det; - sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); - sjack->gpio.debounce_time = 150; - - snd_soc_card_jack_new(card, pin_name, mask, - &sjack->jack, - &sjack->pin, 1); - - snd_soc_jack_add_gpios(&sjack->jack, 1, - &sjack->gpio); - } - - return 0; -} - static int asoc_simple_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -213,14 +146,6 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - ret = asoc_simple_card_init_hp(rtd->card, &priv->hp_jack, PREFIX); - if (ret < 0) - return ret; - - ret = asoc_simple_card_init_mic(rtd->card, &priv->mic_jack, PREFIX); - if (ret < 0) - return ret; - return 0; } @@ -414,6 +339,22 @@ card_parse_end: return ret; } +static int asoc_simple_soc_card_probe(struct snd_soc_card *card) +{ + struct simple_card_data *priv = snd_soc_card_get_drvdata(card); + int ret; + + ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_mic(card, &priv->mic_jack, PREFIX); + if (ret < 0) + return ret; + + return 0; +} + static int asoc_simple_card_probe(struct platform_device *pdev) { struct simple_card_data *priv; @@ -449,6 +390,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) card->dev = dev; card->dai_link = priv->dai_link; card->num_links = num; + card->probe = asoc_simple_soc_card_probe; if (np && of_device_is_available(np)) { diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 487716559deb..16a83bc51e0e 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -1,15 +1,12 @@ -/* - * ASoC simple SCU sound card support - * - * Copyright (C) 2015 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * based on ${LINUX}/sound/soc/generic/simple-card.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ASoC simple SCU sound card support +// +// Copyright (C) 2015 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// based on ${LINUX}/sound/soc/generic/simple-card.c + #include <linux/clk.h> #include <linux/device.h> #include <linux/module.h> diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 6a8b253c58d2..5455d6e0ab53 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -266,17 +266,15 @@ static int sst_cdev_ack(struct device *dev, unsigned int str_id, stream->cumm_bytes += bytes; dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - +(str_id * sizeof(fw_tstamp))), - sizeof(fw_tstamp)); + addr = ((void __iomem *)(ctx->mailbox + ctx->tstamp)) + + (str_id * sizeof(fw_tstamp)); + + memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp)); fw_tstamp.bytes_copied = stream->cumm_bytes; dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n", fw_tstamp.bytes_copied, bytes); - addr = ((void *)(ctx->mailbox + ctx->tstamp)) + - (str_id * sizeof(fw_tstamp)); offset = offsetof(struct snd_sst_tstamp, bytes_copied); sst_shim_write(addr, offset, fw_tstamp.bytes_copied); return 0; @@ -360,11 +358,12 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, struct snd_sst_tstamp fw_tstamp = {0,}; struct stream_info *stream; struct intel_sst_drv *ctx = dev_get_drvdata(dev); + void __iomem *addr; + + addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) + + (str_id * sizeof(fw_tstamp)); - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - +(str_id * sizeof(fw_tstamp))), - sizeof(fw_tstamp)); + memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp)); stream = get_stream_info(ctx, str_id); if (!stream) @@ -530,6 +529,7 @@ static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) struct snd_sst_tstamp fw_tstamp; unsigned int str_id; struct intel_sst_drv *ctx = dev_get_drvdata(dev); + void __iomem *addr; str_id = info->str_id; stream = get_stream_info(ctx, str_id); @@ -540,10 +540,11 @@ static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) return -EINVAL; substream = stream->pcm_substream; - memcpy_fromio(&fw_tstamp, - ((void *)(ctx->mailbox + ctx->tstamp) - + (str_id * sizeof(fw_tstamp))), - sizeof(fw_tstamp)); + addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) + + (str_id * sizeof(fw_tstamp)); + + memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp)); + return sst_calc_tstamp(ctx, info, substream, &fw_tstamp); } diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index a686eef2cf7f..27413ebae956 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -44,15 +44,15 @@ void memcpy32_toio(void __iomem *dst, const void *src, int count) /* __iowrite32_copy uses 32-bit count values so divide by 4 for * right count in words */ - __iowrite32_copy(dst, src, count/4); + __iowrite32_copy(dst, src, count / 4); } void memcpy32_fromio(void *dst, const void __iomem *src, int count) { - /* __iowrite32_copy uses 32-bit count values so divide by 4 for + /* __ioread32_copy uses 32-bit count values so divide by 4 for * right count in words */ - __iowrite32_copy(dst, src, count/4); + __ioread32_copy(dst, src, count / 4); } /** diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 24797482a3d2..cccda87f4b34 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -281,6 +281,20 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH + tristate "GLK with RT5682 and MAX98357A in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5682 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Geminilake platforms + with RT5682 + MAX98357A I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 92b5507291af..87ef8b4058e5 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -6,6 +6,7 @@ snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o +snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o +obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 6ea360f33575..efcfd906c856 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -154,9 +154,7 @@ static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP0 to 16 bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S16_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 40eb979d5ac1..6f052fc8d1e2 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -160,7 +160,7 @@ static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -324,8 +324,22 @@ static const struct snd_pcm_hw_constraint_list constraints_16000 = { .list = rates_16000, }; +static const unsigned int ch_mono[] = { + 1, +}; + +static const struct snd_pcm_hw_constraint_list constraints_refcap = { + .count = ARRAY_SIZE(ch_mono), + .list = ch_mono, +}; + static int broxton_refcap_startup(struct snd_pcm_substream *substream) { + substream->runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_refcap); + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_16000); @@ -586,7 +600,7 @@ static int broxton_audio_probe(struct platform_device *pdev) static struct platform_driver broxton_audio = { .probe = broxton_audio_probe, .driver = { - .name = "bxt_da7219_max98357a_i2s", + .name = "bxt_da7219_max98357a", .pm = &snd_soc_pm_ops, }, }; @@ -599,4 +613,4 @@ MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>"); MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s"); +MODULE_ALIAS("platform:bxt_da7219_max98357a"); diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index b68c289558a8..27308337ab12 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -221,7 +221,7 @@ static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP5 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 33065ba294a9..d32844f94d74 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -404,7 +404,7 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { }, .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | BYT_RT5640_JD_SRC_JD1_IN4P | - BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_TH_1500UA | BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), @@ -464,12 +464,38 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_MCLK_EN), }, { + /* Chuwi Vi10 (CWI505) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-PF02"), + DMI_MATCH(DMI_SYS_VENDOR, "ilife"), + DMI_MATCH(DMI_PRODUCT_NAME, "S165"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), }, .driver_data = (void *)(BYT_RT5640_DMIC1_MAP), }, + { /* Connect Tablet 9 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Connect"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Tablet 9"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -536,6 +562,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), }, + { /* Lenovo Miix 2 8 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "20326"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Hiking"), + }, + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_MCLK_EN), + }, { /* MSI S100 tablet */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), @@ -549,6 +588,20 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_DIFF_MIC | BYT_RT5640_MCLK_EN), }, + { /* Nuvison/TMax TM800W560 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TMAX"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TM800W560L"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { /* Pipo W4 */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 987720e203f9..f8a68bdb3885 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -26,8 +26,12 @@ #include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> +#include <linux/input.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/machine.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -42,8 +46,6 @@ enum { BYT_RT5651_IN1_MAP, BYT_RT5651_IN2_MAP, BYT_RT5651_IN1_IN2_MAP, - BYT_RT5651_IN1_HS_IN3_MAP, - BYT_RT5651_IN2_HS_IN3_MAP, }; enum { @@ -76,21 +78,26 @@ enum { #define BYT_RT5651_SSP2_AIF2 BIT(19) /* default is using AIF1 */ #define BYT_RT5651_SSP0_AIF1 BIT(20) #define BYT_RT5651_SSP0_AIF2 BIT(21) +#define BYT_RT5651_HP_LR_SWAPPED BIT(22) +#define BYT_RT5651_MONO_SPEAKER BIT(23) + +#define BYT_RT5651_DEFAULT_QUIRKS (BYT_RT5651_MCLK_EN | \ + BYT_RT5651_JD1_1 | \ + BYT_RT5651_OVCD_TH_2000UA | \ + BYT_RT5651_OVCD_SF_0P75) /* jack-detect-source + dmic-en + ovcd-th + -sf + terminating empty entry */ #define MAX_NO_PROPS 5 struct byt_rt5651_private { struct clk *mclk; + struct gpio_desc *ext_amp_gpio; struct snd_soc_jack jack; }; /* Default: jack-detect on JD1_1, internal mic on in2, headsetmic on in3 */ -static unsigned long byt_rt5651_quirk = BYT_RT5651_MCLK_EN | - BYT_RT5651_JD1_1 | - BYT_RT5651_OVCD_TH_2000UA | - BYT_RT5651_OVCD_SF_0P75 | - BYT_RT5651_IN2_HS_IN3_MAP; +static unsigned long byt_rt5651_quirk = BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP; static void log_quirks(struct device *dev) { @@ -100,10 +107,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk IN1_MAP enabled"); if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP) dev_info(dev, "quirk IN2_MAP enabled"); - if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_HS_IN3_MAP) - dev_info(dev, "quirk IN1_HS_IN3_MAP enabled"); - if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_HS_IN3_MAP) - dev_info(dev, "quirk IN2_HS_IN3_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_IN2_MAP) + dev_info(dev, "quirk IN1_IN2_MAP enabled"); if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) { dev_info(dev, "quirk realtek,jack-detect-source %ld\n", BYT_RT5651_JDSRC(byt_rt5651_quirk)); @@ -124,6 +129,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk SSP0_AIF1 enabled\n"); if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2) dev_info(dev, "quirk SSP0_AIF2 enabled\n"); + if (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) + dev_info(dev, "quirk MONO_SPEAKER enabled\n"); } #define BYT_CODEC_DAI1 "rt5651-aif1" @@ -211,6 +218,20 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return 0; } +static int rt5651_ext_amp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); + + if (SND_SOC_DAPM_EVENT_ON(event)) + gpiod_set_value_cansleep(priv->ext_amp_gpio, 1); + else + gpiod_set_value_cansleep(priv->ext_amp_gpio, 0); + + return 0; +} + static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -220,7 +241,9 @@ static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - + SND_SOC_DAPM_SUPPLY("Ext Amp Power", SND_SOC_NOPM, 0, 0, + rt5651_ext_amp_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), }; static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { @@ -228,6 +251,7 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { {"Headset Mic", NULL, "Platform Clock"}, {"Internal Mic", NULL, "Platform Clock"}, {"Speaker", NULL, "Platform Clock"}, + {"Speaker", NULL, "Ext Amp Power"}, {"Line In", NULL, "Platform Clock"}, {"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */ @@ -241,38 +265,26 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { }; static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { - {"IN2P", NULL, "Headset Mic"}, {"DMIC L1", NULL, "Internal Mic"}, {"DMIC R1", NULL, "Internal Mic"}, + {"IN3P", NULL, "Headset Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = { {"Internal Mic", NULL, "micbias1"}, {"IN1P", NULL, "Internal Mic"}, - {"IN2P", NULL, "Headset Mic"}, + {"IN3P", NULL, "Headset Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = { {"Internal Mic", NULL, "micbias1"}, - {"IN1P", NULL, "Headset Mic"}, - {"IN2P", NULL, "Internal Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = { - {"Internal Mic", NULL, "micbias1"}, - {"IN1P", NULL, "Internal Mic"}, {"IN2P", NULL, "Internal Mic"}, {"IN3P", NULL, "Headset Mic"}, }; -static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_hs_in3_map[] = { +static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = { {"Internal Mic", NULL, "micbias1"}, {"IN1P", NULL, "Internal Mic"}, - {"IN3P", NULL, "Headset Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_hs_in3_map[] = { - {"Internal Mic", NULL, "micbias1"}, {"IN2P", NULL, "Internal Mic"}, {"IN3P", NULL, "Headset Mic"}, }; @@ -357,46 +369,72 @@ static int byt_rt5651_quirk_cb(const struct dmi_system_id *id) static const struct dmi_system_id byt_rt5651_quirk_table[] = { { + /* Chuwi Hi8 Pro (CWI513) */ .callback = byt_rt5651_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), - DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"), }, - .driver_data = (void *)(BYT_RT5651_IN1_HS_IN3_MAP), + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_HP_LR_SWAPPED | + BYT_RT5651_MONO_SPEAKER), }, { + /* Chuwi Vi8 Plus (CWI519) */ .callback = byt_rt5651_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ADI"), - DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"), + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"), }, - .driver_data = (void *)(BYT_RT5651_MCLK_EN | - BYT_RT5651_IN1_HS_IN3_MAP), + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_HP_LR_SWAPPED | + BYT_RT5651_MONO_SPEAKER), + }, + { + /* I.T.Works TW701, Ployer Momo7w and Trekstor ST70416-6 + * (these all use the same mainboard) */ + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."), + /* Partial match for all of itWORKS.G.WI71C.JGBMRBA, + * TREK.G.WI71C.JGBMRBA0x and MOMO.G.WI71C.MABMRBA02 */ + DMI_MATCH(DMI_BIOS_VERSION, ".G.WI71C."), + }, + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_SSP0_AIF1 | + BYT_RT5651_MONO_SPEAKER), }, { + /* KIANO SlimNote 14.2 */ .callback = byt_rt5651_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), }, - .driver_data = (void *)(BYT_RT5651_MCLK_EN | - BYT_RT5651_JD1_1 | - BYT_RT5651_OVCD_TH_2000UA | - BYT_RT5651_OVCD_SF_0P75 | + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | BYT_RT5651_IN1_IN2_MAP), }, { - /* Chuwi Vi8 Plus (CWI519) */ + /* Minnowboard Max B3 */ .callback = byt_rt5651_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), - DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"), + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (void *)(BYT_RT5651_IN1_MAP), + }, + { + /* Minnowboard Turbot */ + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ADI"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"), }, .driver_data = (void *)(BYT_RT5651_MCLK_EN | - BYT_RT5651_JD1_1 | - BYT_RT5651_OVCD_TH_2000UA | - BYT_RT5651_OVCD_SF_0P75 | - BYT_RT5651_IN2_HS_IN3_MAP), + BYT_RT5651_IN1_MAP), }, { /* VIOS LTH17 */ @@ -405,11 +443,24 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "VIOS"), DMI_MATCH(DMI_PRODUCT_NAME, "LTH17"), }, - .driver_data = (void *)(BYT_RT5651_MCLK_EN | + .driver_data = (void *)(BYT_RT5651_IN1_IN2_MAP | BYT_RT5651_JD1_1 | BYT_RT5651_OVCD_TH_2000UA | BYT_RT5651_OVCD_SF_1P0 | - BYT_RT5651_IN1_IN2_MAP), + BYT_RT5651_MCLK_EN), + }, + { + /* Yours Y8W81 (and others using the same mainboard) */ + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."), + /* Partial match for all devs with a W86C mainboard */ + DMI_MATCH(DMI_BIOS_VERSION, ".F.W86C."), + }, + .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | + BYT_RT5651_IN2_MAP | + BYT_RT5651_SSP0_AIF1 | + BYT_RT5651_MONO_SPEAKER), }, {} }; @@ -418,15 +469,10 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { * Note this MUST be called before snd_soc_register_card(), so that the props * are in place before the codec component driver's probe function parses them. */ -static int byt_rt5651_add_codec_device_props(const char *i2c_dev_name) +static int byt_rt5651_add_codec_device_props(struct device *i2c_dev) { struct property_entry props[MAX_NO_PROPS] = {}; - struct device *i2c_dev; - int ret, cnt = 0; - - i2c_dev = bus_find_device_by_name(&i2c_bus_type, NULL, i2c_dev_name); - if (!i2c_dev) - return -EPROBE_DEFER; + int cnt = 0; props[cnt++] = PROPERTY_ENTRY_U32("realtek,jack-detect-source", BYT_RT5651_JDSRC(byt_rt5651_quirk)); @@ -440,10 +486,7 @@ static int byt_rt5651_add_codec_device_props(const char *i2c_dev_name) if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,dmic-en"); - ret = device_add_properties(i2c_dev, props); - put_device(i2c_dev); - - return ret; + return device_add_properties(i2c_dev, props); } static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) @@ -475,14 +518,6 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) custom_map = byt_rt5651_intmic_in1_in2_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_in2_map); break; - case BYT_RT5651_IN1_HS_IN3_MAP: - custom_map = byt_rt5651_intmic_in1_hs_in3_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_hs_in3_map); - break; - case BYT_RT5651_IN2_HS_IN3_MAP: - custom_map = byt_rt5651_intmic_in2_hs_in3_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_hs_in3_map); - break; default: custom_map = byt_rt5651_intmic_dmic_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map); @@ -546,13 +581,17 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) { ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET, &priv->jack, - bytcr_jack_pins, ARRAY_SIZE(bytcr_jack_pins)); + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, bytcr_jack_pins, + ARRAY_SIZE(bytcr_jack_pins)); if (ret) { dev_err(runtime->dev, "jack creation failed %d\n", ret); return ret; } + snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, + KEY_PLAYPAUSE); + ret = snd_soc_component_set_jack(codec, &priv->jack, NULL); if (ret) return ret; @@ -691,6 +730,48 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { }; /* SoC card */ +static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; +static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ +static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +static char byt_rt5651_long_name[50]; /* = "bytcr-rt5651-*-spk-*-mic[-swapped-hp]" */ + +static int byt_rt5651_suspend(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + + if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) + return 0; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, byt_rt5651_codec_name)) { + dev_dbg(component->dev, "disabling jack detect before suspend\n"); + snd_soc_component_set_jack(component, NULL, NULL); + break; + } + } + + return 0; +} + +static int byt_rt5651_resume(struct snd_soc_card *card) +{ + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + + if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) + return 0; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, byt_rt5651_codec_name)) { + dev_dbg(component->dev, "re-enabling jack detect after resume\n"); + snd_soc_component_set_jack(component, &priv->jack, NULL); + break; + } + } + + return 0; +} + static struct snd_soc_card byt_rt5651_card = { .name = "bytcr-rt5651", .owner = THIS_MODULE, @@ -701,23 +782,86 @@ static struct snd_soc_card byt_rt5651_card = { .dapm_routes = byt_rt5651_audio_map, .num_dapm_routes = ARRAY_SIZE(byt_rt5651_audio_map), .fully_routed = true, + .suspend_pre = byt_rt5651_suspend, + .resume_post = byt_rt5651_resume, }; -static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; -static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ -static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ -static char byt_rt5651_long_name[40]; /* = "bytcr-rt5651-*-spk-*-mic" */ +static const struct x86_cpu_id baytrail_cpu_ids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 }, /* Valleyview */ + {} +}; + +static const struct x86_cpu_id cherrytrail_cpu_ids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT }, /* Braswell */ + {} +}; + +static const struct acpi_gpio_params first_gpio = { 0, 0, false }; +static const struct acpi_gpio_params second_gpio = { 1, 0, false }; + +static const struct acpi_gpio_mapping byt_rt5651_amp_en_first[] = { + { "ext-amp-enable-gpios", &first_gpio, 1 }, + { }, +}; -static bool is_valleyview(void) +static const struct acpi_gpio_mapping byt_rt5651_amp_en_second[] = { + { "ext-amp-enable-gpios", &second_gpio, 1 }, + { }, +}; + +/* + * Some boards have I2cSerialBusV2, GpioIo, GpioInt as ACPI resources, other + * boards may have I2cSerialBusV2, GpioInt, GpioIo instead. We want the + * GpioIo one for the ext-amp-enable-gpio and both count for the index in + * acpi_gpio_params index. So we have 2 different mappings and the code + * below figures out which one to use. + */ +struct byt_rt5651_acpi_resource_data { + int gpio_count; + int gpio_int_idx; +}; + +static int snd_byt_rt5651_acpi_resource(struct acpi_resource *ares, void *arg) { - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; - - if (!x86_match_cpu(cpu_ids)) - return false; - return true; + struct byt_rt5651_acpi_resource_data *data = arg; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return 0; + + if (ares->data.gpio.connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) + data->gpio_int_idx = data->gpio_count; + + data->gpio_count++; + return 0; +} + +static void snd_byt_rt5651_mc_add_amp_en_gpio_mapping(struct device *codec) +{ + struct byt_rt5651_acpi_resource_data data = { 0, -1 }; + LIST_HEAD(resources); + int ret; + + ret = acpi_dev_get_resources(ACPI_COMPANION(codec), &resources, + snd_byt_rt5651_acpi_resource, &data); + if (ret < 0) { + dev_warn(codec, "Failed to get ACPI resources, not adding external amplifier GPIO mapping\n"); + return; + } + + /* All info we need is gathered during the walk */ + acpi_dev_free_resource_list(&resources); + + switch (data.gpio_int_idx) { + case 0: + devm_acpi_dev_add_driver_gpios(codec, byt_rt5651_amp_en_second); + break; + case 1: + devm_acpi_dev_add_driver_gpios(codec, byt_rt5651_amp_en_first); + break; + default: + dev_warn(codec, "Unknown GpioInt index %d, not adding external amplifier GPIO mapping\n", + data.gpio_int_idx); + } } struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ @@ -727,13 +871,12 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { - const char * const intmic_name[] = - { "dmic", "in1", "in2", "in12", "in1", "in2" }; - const char * const hsmic_name[] = - { "in2", "in2", "in1", "in3", "in3", "in3" }; + const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; struct byt_rt5651_private *priv; struct snd_soc_acpi_mach *mach; + struct device *codec_dev; const char *i2c_name = NULL; + const char *hp_swapped; bool is_bytcr = false; int ret_val = 0; int dai_index = 0; @@ -767,11 +910,16 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) "%s%s", "i2c-", i2c_name); byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; + codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, + byt_rt5651_codec_name); + if (!codec_dev) + return -EPROBE_DEFER; + /* * swap SSP0 if bytcr is detected * (will be overridden if DMI quirk is detected) */ - if (is_valleyview()) { + if (x86_match_cpu(baytrail_cpu_ids)) { struct sst_platform_info *p_info = mach->pdata; const struct sst_res_info *res_info = p_info->res_info; @@ -830,9 +978,37 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) dmi_check_system(byt_rt5651_quirk_table); /* Must be called before register_card, also see declaration comment. */ - ret_val = byt_rt5651_add_codec_device_props(byt_rt5651_codec_name); - if (ret_val) + ret_val = byt_rt5651_add_codec_device_props(codec_dev); + if (ret_val) { + put_device(codec_dev); return ret_val; + } + + /* Cherry Trail devices use an external amplifier enable gpio */ + if (x86_match_cpu(cherrytrail_cpu_ids)) { + snd_byt_rt5651_mc_add_amp_en_gpio_mapping(codec_dev); + priv->ext_amp_gpio = devm_fwnode_get_index_gpiod_from_child( + &pdev->dev, "ext-amp-enable", 0, + codec_dev->fwnode, + GPIOD_OUT_LOW, "speaker-amp"); + if (IS_ERR(priv->ext_amp_gpio)) { + ret_val = PTR_ERR(priv->ext_amp_gpio); + switch (ret_val) { + case -ENOENT: + priv->ext_amp_gpio = NULL; + break; + default: + dev_err(&pdev->dev, "Failed to get ext-amp-enable GPIO: %d\n", + ret_val); + /* fall through */ + case -EPROBE_DEFER: + put_device(codec_dev); + return ret_val; + } + } + } + + put_device(codec_dev); log_quirks(&pdev->dev); @@ -876,10 +1052,16 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } } + if (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) + hp_swapped = "-hp-swapped"; + else + hp_swapped = ""; + snprintf(byt_rt5651_long_name, sizeof(byt_rt5651_long_name), - "bytcr-rt5651-%s-intmic-%s-hsmic", - intmic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], - hsmic_name[BYT_RT5651_MAP(byt_rt5651_quirk)]); + "bytcr-rt5651-%s-spk-%s-mic%s", + (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ? + "mono" : "stereo", + mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], hp_swapped); byt_rt5651_card.long_name = byt_rt5651_long_name; ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c new file mode 100644 index 000000000000..c4b94e2617c5 --- /dev/null +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation. + +/* + * Intel Geminilake I2S Machine Driver with MAX98357A & RT5682 Codecs + * + * Modified from: + * Intel Apollolake I2S Machine driver + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../skylake/skl.h" +#include "../../codecs/rt5682.h" +#include "../../codecs/hdac_hdmi.h" + +/* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */ +#define GLK_PLAT_CLK_FREQ 19200000 +#define RT5682_PLL_FREQ (48000 * 512) +#define GLK_REALTEK_CODEC_DAI "rt5682-aif1" +#define GLK_MAXIM_CODEC_DAI "HiFi" +#define MAXIM_DEV0_NAME "MX98357A:00" +#define DUAL_CHANNEL 2 +#define QUAD_CHANNEL 4 +#define NAME_SIZE 32 + +static struct snd_soc_jack geminilake_hdmi[3]; + +struct glk_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct glk_card_private { + struct snd_soc_jack geminilake_headset; + struct list_head hdmi_pcm_list; +}; + +enum { + GLK_DPCM_AUDIO_PB = 0, + GLK_DPCM_AUDIO_CP, + GLK_DPCM_AUDIO_HS_PB, + GLK_DPCM_AUDIO_ECHO_REF_CP, + GLK_DPCM_AUDIO_REF_CP, + GLK_DPCM_AUDIO_DMIC_CP, + GLK_DPCM_AUDIO_HDMI1_PB, + GLK_DPCM_AUDIO_HDMI2_PB, + GLK_DPCM_AUDIO_HDMI3_PB, +}; + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret = 0; + + codec_dai = snd_soc_card_get_codec_dai(card, GLK_REALTEK_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); + if (ret) + dev_err(card->dev, "failed to stop sysclk: %d\n", ret); + } else if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, + GLK_PLAT_CLK_FREQ, RT5682_PLL_FREQ); + if (ret < 0) { + dev_err(card->dev, "can't set codec pll: %d\n", ret); + return ret; + } + } + + if (ret) + dev_err(card->dev, "failed to start internal clk: %d\n", ret); + + return ret; +} + +static const struct snd_kcontrol_new geminilake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget geminilake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route geminilake_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* speaker */ + { "Spk", NULL, "Speaker" }, + + /* other jacks */ + { "Headset Mic", NULL, "Platform Clock" }, + { "IN1P", NULL, "Headset Mic" }, + + /* digital mics */ + { "DMic", NULL, "SoC DMIC" }, + + /* CODEC BE connections */ + { "HiFi Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec0_out" }, + + { "AIF1 Playback", NULL, "ssp2 Tx" }, + { "ssp2 Tx", NULL, "codec1_out" }, + + { "codec0_in", NULL, "ssp2 Rx" }, + { "ssp2 Rx", NULL, "AIF1 Capture" }, + + { "HDMI1", NULL, "hif5-0 Output" }, + { "HDMI2", NULL, "hif6-0 Output" }, + { "HDMI2", NULL, "hif7-0 Output" }, + + { "hifi3", NULL, "iDisp3 Tx" }, + { "iDisp3 Tx", NULL, "iDisp3_out" }, + { "hifi2", NULL, "iDisp2 Tx" }, + { "iDisp2 Tx", NULL, "iDisp2_out" }, + { "hifi1", NULL, "iDisp1 Tx" }, + { "iDisp1 Tx", NULL, "iDisp1_out" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, +}; + +static int geminilake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_jack *jack; + int ret; + + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, + &ctx->geminilake_headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->geminilake_headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) { + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + return ret; + } + + return ret; +}; + +static int geminilake_rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + /* Set valid bitmask & configuration for I2S in 24 bit */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + + return ret; +} + +static struct snd_soc_ops geminilake_rt5682_ops = { + .hw_params = geminilake_rt5682_hw_params, +}; + +static int geminilake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct glk_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = GLK_DPCM_AUDIO_HDMI1_PB + dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int geminilake_rt5682_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = rtd->cpu_dai->component; + struct snd_soc_dapm_context *dapm; + int ret; + + dapm = snd_soc_component_get_dapm(component); + ret = snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + if (ret) { + dev_err(rtd->dev, "Ref Cap ignore suspend failed %d\n", ret); + return ret; + } + + return ret; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static unsigned int channels_quad[] = { + QUAD_CHANNEL, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels_quad = { + .count = ARRAY_SIZE(channels_quad), + .list = channels_quad, + .mask = 0, +}; + +static int geminilake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* + * set BE channel constraint as user FE channels + */ + channels->min = channels->max = 4; + + return 0; +} + +static int geminilake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels_quad); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops geminilake_dmic_ops = { + .startup = geminilake_dmic_startup, +}; + +static const unsigned int rates_16000[] = { + 16000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int geminilake_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +}; + +static const struct snd_soc_ops geminilake_refcap_ops = { + .startup = geminilake_refcap_startup, +}; + +/* geminilake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link geminilake_dais[] = { + /* Front End DAI links */ + [GLK_DPCM_AUDIO_PB] = { + .name = "Glk Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = geminilake_rt5682_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + [GLK_DPCM_AUDIO_CP] = { + .name = "Glk Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + [GLK_DPCM_AUDIO_HS_PB] = { + .name = "Glk Audio Headset Playback", + .stream_name = "Headset Audio", + .cpu_dai_name = "System Pin2", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dpcm_playback = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_ECHO_REF_CP] = { + .name = "Glk Audio Echo Reference cap", + .stream_name = "Echoreference Capture", + .cpu_dai_name = "Echoref Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .capture_only = 1, + .nonatomic = 1, + }, + [GLK_DPCM_AUDIO_REF_CP] = { + .name = "Glk Audio Reference cap", + .stream_name = "Refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &geminilake_refcap_ops, + }, + [GLK_DPCM_AUDIO_DMIC_CP] = { + .name = "Glk Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &geminilake_dmic_ops, + }, + [GLK_DPCM_AUDIO_HDMI1_PB] = { + .name = "Glk HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_HDMI2_PB] = { + .name = "Glk HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [GLK_DPCM_AUDIO_HDMI3_PB] = { + .name = "Glk HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 0, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = MAXIM_DEV0_NAME, + .codec_dai_name = GLK_MAXIM_CODEC_DAI, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = geminilake_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 1, + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "i2c-10EC5682:00", + .codec_dai_name = GLK_REALTEK_CODEC_DAI, + .init = geminilake_rt5682_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = geminilake_ssp_fixup, + .ops = &geminilake_rt5682_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .be_hw_params_fixup = geminilake_dmic_fixup, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = geminilake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int glk_card_late_probe(struct snd_soc_card *card) +{ + struct glk_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[NAME_SIZE]; + struct glk_hdmi_pcm *pcm; + int err = 0; + int i = 0; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &geminilake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &geminilake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +/* geminilake audio machine driver for SPT + RT5682 */ +static struct snd_soc_card glk_audio_card_rt5682_m98357a = { + .name = "glkrt5682max", + .owner = THIS_MODULE, + .dai_link = geminilake_dais, + .num_links = ARRAY_SIZE(geminilake_dais), + .controls = geminilake_controls, + .num_controls = ARRAY_SIZE(geminilake_controls), + .dapm_widgets = geminilake_widgets, + .num_dapm_widgets = ARRAY_SIZE(geminilake_widgets), + .dapm_routes = geminilake_map, + .num_dapm_routes = ARRAY_SIZE(geminilake_map), + .fully_routed = true, + .late_probe = glk_card_late_probe, +}; + +static int geminilake_audio_probe(struct platform_device *pdev) +{ + struct glk_card_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + glk_audio_card_rt5682_m98357a.dev = &pdev->dev; + snd_soc_card_set_drvdata(&glk_audio_card_rt5682_m98357a, ctx); + + return devm_snd_soc_register_card(&pdev->dev, + &glk_audio_card_rt5682_m98357a); +} + +static const struct platform_device_id glk_board_ids[] = { + { + .name = "glk_rt5682_max98357a", + .driver_data = + (kernel_ulong_t)&glk_audio_card_rt5682_m98357a, + }, + { } +}; + +static struct platform_driver geminilake_audio = { + .probe = geminilake_audio_probe, + .driver = { + .name = "glk_rt5682_max98357a", + .pm = &snd_soc_pm_ops, + }, + .id_table = glk_board_ids, +}; +module_platform_driver(geminilake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Geminilake Audio Machine driver-RT5682 & MAX98357A in I2S mode"); +MODULE_AUTHOR("Naveen Manohar <naveen.m@intel.com>"); +MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:glk_rt5682_max98357a"); diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 94294c27d1db..38f6ab74709d 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -152,7 +152,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -380,6 +380,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, + .ops = &kabylake_da7219_fe_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 3a61252fe450..21a6490746a6 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -434,14 +434,14 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } /* * The speaker on the SSP0 supports S16_LE and not S24_LE. * thus changing the mask here */ if (!strcmp(be_dai_link->name, "SSP0-Codec")) - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 92f5fb2ae0a3..a892b37eab7c 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -307,7 +307,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); } else if (!strcmp(fe_dai_link->name, "Kbl Audio DMIC cap")) { if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) @@ -320,7 +320,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, * thus changing the mask here */ if (!strcmp(be_dai_link->name, "SSP0-Codec")) - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 3ff6646cfa21..d31482b8c9bb 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -157,7 +157,7 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP0 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index b0610bba3cfa..e877bb60beb1 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -346,7 +346,7 @@ static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP0 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 38a1495c29cf..0e1818dd4cc6 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -229,7 +229,7 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP0 to 24 bit */ snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 7379d8830c39..915a34cdc8ac 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -3,7 +3,11 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o -snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ + soc-acpi-intel-hsw-bdw-match.o \ + soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ + soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ + soc-acpi-intel-cnl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c new file mode 100644 index 000000000000..f39386e540d3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-bxt-match.c - tables and support for BXT ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "104C5122", + .drv_name = "bxt-pcm512x", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-pcm512x.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "1AEC8804", + .drv_name = "bxt-wm8804", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index bfe1ca68a542..4daa8a4f0c0c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -59,8 +59,8 @@ static struct snd_soc_acpi_mach byt_thinkpad_10 = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -98,8 +98,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", .machine_quirk = byt_quirk, - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -107,8 +107,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -116,8 +116,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -125,8 +125,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -134,8 +134,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -143,8 +143,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ @@ -153,8 +153,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -162,8 +162,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* use CHT driver to Baytrail Chromebooks */ @@ -172,8 +172,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-byt.ri", - .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .sof_fw_filename = "intel/sof-byt.ri", + .sof_tplg_filename = "intel/sof-byt-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index ad1eb2d644be..91bb99b69601 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -44,8 +44,8 @@ static struct snd_soc_acpi_mach cht_surface_mach = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }; @@ -68,8 +68,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -77,8 +77,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5670.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -86,8 +86,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -95,8 +95,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -104,8 +104,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5645.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -113,8 +113,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-max98090", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-max98090.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -122,8 +122,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "cht-bsw-nau8824", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-nau8824.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-nau8824.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -131,8 +131,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -140,8 +140,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-da7213.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -149,8 +149,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcht_es8316", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_es8316", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-es8316.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ @@ -160,8 +160,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", .machine_quirk = cht_quirk, - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, { @@ -169,8 +169,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5640.tplg", .asoc_plat_name = "sst-mfld-platform", }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ @@ -179,8 +179,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .drv_name = "bytcr_rt5651", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5651", - .sof_fw_filename = "intel/reef-cht.ri", - .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .sof_fw_filename = "intel/sof-cht.ri", + .sof_tplg_filename = "intel/sof-cht-rt5651.tplg", .asoc_plat_name = "sst-mfld-platform", }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c new file mode 100644 index 000000000000..ec8e28e7b937 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-cnl-match.c - tables and support for CNL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + .sof_fw_filename = "intel/sof-cnl.ri", + .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c new file mode 100644 index 000000000000..305875af71ca --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-glk-match.c - tables and support for GLK ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static struct snd_soc_acpi_codecs glk_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-alc298.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + { + .id = "DLGS7219", + .drv_name = "glk_da7219_max98357a", + .fw_filename = "intel/dsp_fw_glk.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + .sof_fw_filename = "intel/sof-glk.ri", + .sof_tplg_filename = "intel/sof-glk-da7219.tplg", + .asoc_plat_name = "0000:00:0e.0", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c index e0e8c8c27528..494a0ea9b029 100644 --- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -23,8 +23,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST1.bin", - .sof_fw_filename = "intel/reef-hsw.ri", - .sof_tplg_filename = "intel/reef-hsw.tplg", + .sof_fw_filename = "intel/sof-hsw.ri", + .sof_tplg_filename = "intel/sof-hsw.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} @@ -36,24 +36,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { .id = "INT343A", .drv_name = "broadwell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt286.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "RT5677CE", .drv_name = "bdw-rt5677", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5677.tplg", .asoc_plat_name = "haswell-pcm-audio", }, { .id = "INT33CA", .drv_name = "haswell-audio", .fw_filename = "intel/IntcSST2.bin", - .sof_fw_filename = "intel/reef-bdw.ri", - .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .sof_fw_filename = "intel/sof-bdw.ri", + .sof_tplg_filename = "intel/sof-bdw-rt5640.tplg", .asoc_plat_name = "haswell-pcm-audio", }, {} diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c new file mode 100644 index 000000000000..0ee173ca437d --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-kbl-match.c - tables and support for KBL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c new file mode 100644 index 000000000000..0c9c0edd35b3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-skl-match.c - tables and support for SKL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata skl_dmic_data; + +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_skl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 657afc02f1c4..11041aedea31 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -270,7 +270,7 @@ void sst_dsp_dma_put_channel(struct sst_dsp *dsp) } EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel); -int sst_dma_new(struct sst_dsp *sst) +static int sst_dma_new(struct sst_dsp *sst) { struct sst_pdata *sst_pdata = sst->pdata; struct sst_dma *dma; @@ -320,9 +320,8 @@ err_dma_dev: devm_kfree(sst->dev, dma); return ret; } -EXPORT_SYMBOL(sst_dma_new); -void sst_dma_free(struct sst_dma *dma) +static void sst_dma_free(struct sst_dma *dma) { if (dma == NULL) @@ -335,7 +334,6 @@ void sst_dma_free(struct sst_dma *dma) dw_remove(dma->chip); } -EXPORT_SYMBOL(sst_dma_free); /* create new generic firmware object */ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, diff --git a/sound/soc/intel/haswell/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c index b2bec36d074c..a28220e67cdf 100644 --- a/sound/soc/intel/haswell/sst-haswell-dsp.c +++ b/sound/soc/intel/haswell/sst-haswell-dsp.c @@ -93,29 +93,31 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, struct sst_module_template template; int count, ret; void __iomem *ram; + int type = le16_to_cpu(module->type); + int entry_point = le32_to_cpu(module->entry_point); /* TODO: allowed module types need to be configurable */ - if (module->type != SST_HSW_MODULE_BASE_FW - && module->type != SST_HSW_MODULE_PCM_SYSTEM - && module->type != SST_HSW_MODULE_PCM - && module->type != SST_HSW_MODULE_PCM_REFERENCE - && module->type != SST_HSW_MODULE_PCM_CAPTURE - && module->type != SST_HSW_MODULE_WAVES - && module->type != SST_HSW_MODULE_LPAL) + if (type != SST_HSW_MODULE_BASE_FW && + type != SST_HSW_MODULE_PCM_SYSTEM && + type != SST_HSW_MODULE_PCM && + type != SST_HSW_MODULE_PCM_REFERENCE && + type != SST_HSW_MODULE_PCM_CAPTURE && + type != SST_HSW_MODULE_WAVES && + type != SST_HSW_MODULE_LPAL) return 0; dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n", module->signature, module->mod_size, - module->blocks, module->type); - dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point); + module->blocks, type); + dev_dbg(dsp->dev, " entrypoint 0x%x\n", entry_point); dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n", module->info.persistent_size, module->info.scratch_size); memset(&template, 0, sizeof(template)); - template.id = module->type; - template.entry = module->entry_point - 4; - template.persistent_size = module->info.persistent_size; - template.scratch_size = module->info.scratch_size; + template.id = type; + template.entry = entry_point - 4; + template.persistent_size = le32_to_cpu(module->info.persistent_size); + template.scratch_size = le32_to_cpu(module->info.scratch_size); mod = sst_module_new(fw, &template, NULL); if (mod == NULL) @@ -123,26 +125,26 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, block = (void *)module + sizeof(*module); - for (count = 0; count < module->blocks; count++) { + for (count = 0; count < le32_to_cpu(module->blocks); count++) { - if (block->size <= 0) { + if (le32_to_cpu(block->size) <= 0) { dev_err(dsp->dev, "error: block %d size invalid\n", count); sst_module_free(mod); return -EINVAL; } - switch (block->type) { + switch (le32_to_cpu(block->type)) { case SST_HSW_IRAM: ram = dsp->addr.lpe; - mod->offset = - block->ram_offset + dsp->addr.iram_offset; + mod->offset = le32_to_cpu(block->ram_offset) + + dsp->addr.iram_offset; mod->type = SST_MEM_IRAM; break; case SST_HSW_DRAM: case SST_HSW_REGS: ram = dsp->addr.lpe; - mod->offset = block->ram_offset; + mod->offset = le32_to_cpu(block->ram_offset); mod->type = SST_MEM_DRAM; break; default: @@ -152,7 +154,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, return -EINVAL; } - mod->size = block->size; + mod->size = le32_to_cpu(block->size); mod->data = (void *)block + sizeof(*block); mod->data_offset = mod->data - fw->dma_buf; @@ -169,7 +171,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, return ret; } - block = (void *)block + sizeof(*block) + block->size; + block = (void *)block + sizeof(*block) + + le32_to_cpu(block->size); } mod->state = SST_MODULE_STATE_LOADED; @@ -188,7 +191,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) /* verify FW */ if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) || - (sst_fw->size != header->file_size + sizeof(*header))) { + (sst_fw->size != + le32_to_cpu(header->file_size) + sizeof(*header))) { dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n"); return -EINVAL; } @@ -199,7 +203,7 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) /* parse each module */ module = (void *)sst_fw->dma_buf + sizeof(*header); - for (count = 0; count < header->modules; count++) { + for (count = 0; count < le32_to_cpu(header->modules); count++) { /* module */ ret = hsw_parse_module(dsp, sst_fw, module); @@ -207,7 +211,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw) dev_err(dsp->dev, "error: invalid module %d\n", count); return ret; } - module = (void *)module + sizeof(*module) + module->mod_size; + module = (void *)module + sizeof(*module) + + le32_to_cpu(module->mod_size); } return 0; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index d7fc3b2d3e68..823e39103edd 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1014,10 +1014,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai) +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - pcm_dai->ops = &skl_pcm_dai_ops; + dai_drv->ops = &skl_pcm_dai_ops; return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index d2b1d60fec02..5bc0d38da7e3 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -83,9 +83,9 @@ static void skl_cldma_stream_clear(struct sst_dsp *ctx) /* Code loader helper APIs */ static void skl_cldma_setup_bdle(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_data, - u32 **bdlp, int size, int with_ioc) + __le32 **bdlp, int size, int with_ioc) { - u32 *bdl = *bdlp; + __le32 *bdl = *bdlp; ctx->cl_dev.frags = 0; while (size > 0) { @@ -330,7 +330,7 @@ void skl_cldma_process_intr(struct sst_dsp *ctx) int skl_cldma_prepare(struct sst_dsp *ctx) { int ret; - u32 *bdl; + __le32 *bdl; ctx->cl_dev.bufsize = SKL_MAX_BUFFER_SIZE; @@ -359,7 +359,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); return ret; } - bdl = (u32 *)ctx->cl_dev.dmab_bdl.area; + bdl = (__le32 *)ctx->cl_dev.dmab_bdl.area; /* Allocate BDLs */ ctx->cl_dev.ops.cl_setup_bdle(ctx, &ctx->cl_dev.dmab_data, diff --git a/sound/soc/intel/skylake/skl-sst-cldma.h b/sound/soc/intel/skylake/skl-sst-cldma.h index 5b730a1a0ae4..ec736921a083 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.h +++ b/sound/soc/intel/skylake/skl-sst-cldma.h @@ -203,7 +203,7 @@ struct sst_dsp; struct skl_cl_dev_ops { void (*cl_setup_bdle)(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_data, - u32 **bdlp, int size, int with_ioc); + __le32 **bdlp, int size, int with_ioc); void (*cl_setup_controller)(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_bdl, unsigned int max_size, u32 page_count); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index abfdb67c05cc..2620d77729c5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -108,6 +108,9 @@ static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_out: case snd_soc_dapm_switch: + case snd_soc_dapm_output: + case snd_soc_dapm_mux: + return false; default: return true; @@ -3024,7 +3027,7 @@ void skl_cleanup_resources(struct skl *skl) * information to the driver about module and pipeline parameters which DSP * FW expects like ids, resource values, formats etc */ -static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, +static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { @@ -3130,6 +3133,7 @@ static int skl_init_enum_data(struct device *dev, struct soc_enum *se, } static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + int index, struct snd_kcontrol_new *kctl, struct snd_soc_tplg_ctl_hdr *hdr) { @@ -3617,7 +3621,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, return 0; } -static int skl_manifest_load(struct snd_soc_component *cmpnt, +static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_tplg_manifest *manifest) { struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index daeb6d2bb7fc..82282cac9751 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -512,8 +512,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); +int skl_dai_load(struct snd_soc_component *cmp, int index, + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 00e051467a40..dce649485649 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -29,6 +29,7 @@ #include <linux/delay.h> #include <sound/pcm.h> #include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> #include <sound/hda_register.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> @@ -36,8 +37,6 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" -static struct skl_machine_pdata skl_dmic_data; - /* * initialize the PCI registers */ @@ -487,10 +486,12 @@ static int skl_find_machine(struct skl *skl, void *driver_data) skl->mach = mach; skl->fw_name = mach->fw_filename; - pdata = skl->mach->pdata; + pdata = mach->pdata; - if (mach->pdata) + if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; + pdata->dmic_num = skl_get_dmic_geo(skl); + } return 0; } @@ -917,8 +918,6 @@ static int skl_probe(struct pci_dev *pci, pci_set_drvdata(skl->pci, bus); - skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); - /* check if dsp is there */ if (bus->ppcap) { /* create device for dsp clk */ @@ -1012,172 +1011,23 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", - .drv_name = "bxt_alc298s_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, - {} -}; - -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", - .drv_name = "kbl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&sst_skl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = (unsigned long)&sst_bxtp_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), - .driver_data = (unsigned long)&sst_kbl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, /* GLK */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = (unsigned long)&sst_glk_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = (unsigned long)&sst_cnl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 51ec4ff6ed95..697aa50aff9a 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -15,20 +15,12 @@ int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe) { - struct snd_soc_dai_driver *sub_dai_drivers; + struct mtk_base_afe_dai *dai; size_t num_dai_drivers = 0, dai_idx = 0; - int i; - - if (!afe->sub_dais) { - dev_err(afe->dev, "%s(), sub_dais == NULL\n", __func__); - return -EINVAL; - } /* calcualte total dai driver size */ - for (i = 0; i < afe->num_sub_dais; i++) { - if (afe->sub_dais[i].dai_drivers && - afe->sub_dais[i].num_dai_drivers != 0) - num_dai_drivers += afe->sub_dais[i].num_dai_drivers; + list_for_each_entry(dai, &afe->sub_dais, list) { + num_dai_drivers += dai->num_dai_drivers; } dev_info(afe->dev, "%s(), num of dai %zd\n", __func__, num_dai_drivers); @@ -42,19 +34,14 @@ int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe) if (!afe->dai_drivers) return -ENOMEM; - for (i = 0; i < afe->num_sub_dais; i++) { - if (afe->sub_dais[i].dai_drivers && - afe->sub_dais[i].num_dai_drivers != 0) { - sub_dai_drivers = afe->sub_dais[i].dai_drivers; - /* dai driver */ - memcpy(&afe->dai_drivers[dai_idx], - sub_dai_drivers, - afe->sub_dais[i].num_dai_drivers * - sizeof(struct snd_soc_dai_driver)); - dai_idx += afe->sub_dais[i].num_dai_drivers; - } + list_for_each_entry(dai, &afe->sub_dais, list) { + /* dai driver */ + memcpy(&afe->dai_drivers[dai_idx], + dai->dai_drivers, + dai->num_dai_drivers * + sizeof(struct snd_soc_dai_driver)); + dai_idx += dai->num_dai_drivers; } - return 0; } EXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai); @@ -62,28 +49,25 @@ EXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai); int mtk_afe_add_sub_dai_control(struct snd_soc_component *component) { struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); - int i; + struct mtk_base_afe_dai *dai; - if (!afe->sub_dais) { - dev_err(afe->dev, "%s(), sub_dais == NULL\n", __func__); - return -EINVAL; - } - - for (i = 0; i < afe->num_sub_dais; i++) { - if (afe->sub_dais[i].controls) + list_for_each_entry(dai, &afe->sub_dais, list) { + if (dai->controls) snd_soc_add_component_controls(component, - afe->sub_dais[i].controls, - afe->sub_dais[i].num_controls); + dai->controls, + dai->num_controls); - if (afe->sub_dais[i].dapm_widgets) + if (dai->dapm_widgets) snd_soc_dapm_new_controls(&component->dapm, - afe->sub_dais[i].dapm_widgets, - afe->sub_dais[i].num_dapm_widgets); - - if (afe->sub_dais[i].dapm_routes) + dai->dapm_widgets, + dai->num_dapm_widgets); + } + /* add routes after all widgets are added */ + list_for_each_entry(dai, &afe->sub_dais, list) { + if (dai->dapm_routes) snd_soc_dapm_add_routes(&component->dapm, - afe->sub_dais[i].dapm_routes, - afe->sub_dais[i].num_dapm_routes); + dai->dapm_routes, + dai->num_dapm_routes); } snd_soc_dapm_new_widgets(component->dapm.card); diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index bcf562f029b6..bd8d5e0c6843 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -46,6 +46,7 @@ struct mtk_base_irq_data { }; struct device; +struct list_head; struct mtk_base_afe_memif; struct mtk_base_afe_irq; struct mtk_base_afe_dai; @@ -72,8 +73,7 @@ struct mtk_base_afe { struct mtk_base_afe_irq *irqs; int irqs_size; - struct mtk_base_afe_dai *sub_dais; - int num_sub_dais; + struct list_head sub_dais; struct snd_soc_dai_driver *dai_drivers; unsigned int num_dai_drivers; @@ -110,6 +110,8 @@ struct mtk_base_afe_dai { unsigned int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; unsigned int num_dapm_routes; + + struct list_head list; }; #endif diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-common.h b/sound/soc/mediatek/mt6797/mt6797-afe-common.h index 22eb7b455cf1..4eac9977b2b0 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-common.h +++ b/sound/soc/mediatek/mt6797/mt6797-afe-common.h @@ -10,6 +10,7 @@ #define _MT_6797_AFE_COMMON_H_ #include <sound/soc.h> +#include <linux/list.h> #include <linux/regmap.h> #include "../common/mtk-base-afe.h" diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 6c5dd9fc9976..192f4d7b37b6 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -733,6 +733,34 @@ static const struct snd_soc_component_driver mt6797_afe_component = { .probe = mt6797_afe_component_probe, }; +static int mt6797_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt6797_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt6797_memif_dai_driver); + + dai->dapm_widgets = mt6797_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt6797_memif_widgets); + dai->dapm_routes = mt6797_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt6797_memif_routes); + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt6797_dai_adda_register, + mt6797_dai_pcm_register, + mt6797_dai_hostless_register, + mt6797_dai_memif_register, +}; + static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -811,29 +839,24 @@ static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) } /* init sub_dais */ - afe->num_sub_dais = MT6797_DAI_NUM; - afe->sub_dais = devm_kcalloc(dev, afe->num_sub_dais, - sizeof(*afe->sub_dais), - GFP_KERNEL); - if (!afe->sub_dais) - return -ENOMEM; - - mt6797_dai_adda_register(afe); - mt6797_dai_pcm_register(afe); - mt6797_dai_hostless_register(afe); - - afe->sub_dais[MT6797_MEMIF_DL1].dai_drivers = mt6797_memif_dai_driver; - afe->sub_dais[MT6797_MEMIF_DL1].num_dai_drivers = - ARRAY_SIZE(mt6797_memif_dai_driver); - afe->sub_dais[MT6797_MEMIF_DL1].dapm_widgets = mt6797_memif_widgets; - afe->sub_dais[MT6797_MEMIF_DL1].num_dapm_widgets = - ARRAY_SIZE(mt6797_memif_widgets); - afe->sub_dais[MT6797_MEMIF_DL1].dapm_routes = mt6797_memif_routes; - afe->sub_dais[MT6797_MEMIF_DL1].num_dapm_routes = - ARRAY_SIZE(mt6797_memif_routes); + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) { + dev_warn(afe->dev, "dai register i %d fail, ret %d\n", + i, ret); + return ret; + } + } /* init dai_driver and component_driver */ - mtk_afe_combine_sub_dai(afe); + ret = mtk_afe_combine_sub_dai(afe); + if (ret) { + dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n", + ret); + return ret; + } afe->mtk_afe_hardware = &mt6797_afe_hardware; afe->memif_fs = mt6797_memif_fs; diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-adda.c b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c index ad083265ce94..0ac6409c6d61 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-adda.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c @@ -383,14 +383,20 @@ static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { int mt6797_dai_adda_register(struct mtk_base_afe *afe) { - int id = MT6797_DAI_ADDA; + struct mtk_base_afe_dai *dai; - afe->sub_dais[id].dai_drivers = mtk_dai_adda_driver; - afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; - afe->sub_dais[id].dapm_widgets = mtk_dai_adda_widgets; - afe->sub_dais[id].num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); - afe->sub_dais[id].dapm_routes = mtk_dai_adda_routes; - afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_adda_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + + dai->dapm_widgets = mtk_dai_adda_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); + dai->dapm_routes = mtk_dai_adda_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); return 0; } diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c index 4cf985b15a11..ed23e6a53b08 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c @@ -100,13 +100,19 @@ static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = { int mt6797_dai_hostless_register(struct mtk_base_afe *afe) { - int id = MT6797_DAI_HOSTLESS_LPBK; + struct mtk_base_afe_dai *dai; - afe->sub_dais[id].dai_drivers = mtk_dai_hostless_driver; - afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver); + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; - afe->sub_dais[id].dapm_routes = mtk_dai_hostless_routes; - afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes); + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_hostless_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver); + + dai->dapm_routes = mtk_dai_hostless_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes); return 0; } diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c index 16d5b5067204..3136f0bc7827 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c @@ -298,15 +298,20 @@ static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { int mt6797_dai_pcm_register(struct mtk_base_afe *afe) { - int id = MT6797_DAI_PCM_1; + struct mtk_base_afe_dai *dai; - afe->sub_dais[id].dai_drivers = mtk_dai_pcm_driver; - afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; - afe->sub_dais[id].dapm_widgets = mtk_dai_pcm_widgets; - afe->sub_dais[id].num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); - afe->sub_dais[id].dapm_routes = mtk_dai_pcm_routes; - afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); + list_add(&dai->list, &afe->sub_dais); + dai->dai_drivers = mtk_dai_pcm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + + dai->dapm_widgets = mtk_dai_pcm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); + dai->dapm_routes = mtk_dai_pcm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); return 0; } diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig new file mode 100644 index 000000000000..8af8bc358a90 --- /dev/null +++ b/sound/soc/meson/Kconfig @@ -0,0 +1,65 @@ +menu "ASoC support for Amlogic platforms" + depends on ARCH_MESON || COMPILE_TEST + +config SND_MESON_AXG_FIFO + tristate + select REGMAP_MMIO + +config SND_MESON_AXG_FRDDR + tristate "Amlogic AXG Playback FIFO support" + select SND_MESON_AXG_FIFO + help + Select Y or M to add support for the frontend playback interfaces + embedded in the Amlogic AXG SoC family + +config SND_MESON_AXG_TODDR + tristate "Amlogic AXG Capture FIFO support" + select SND_MESON_AXG_FIFO + help + Select Y or M to add support for the frontend capture interfaces + embedded in the Amlogic AXG SoC family + +config SND_MESON_AXG_TDM_FORMATTER + tristate + select REGMAP_MMIO + +config SND_MESON_AXG_TDM_INTERFACE + tristate + select SND_MESON_AXG_TDM_FORMATTER + +config SND_MESON_AXG_TDMIN + tristate "Amlogic AXG TDM Input Support" + select SND_MESON_AXG_TDM_FORMATTER + select SND_MESON_AXG_TDM_INTERFACE + help + Select Y or M to add support for TDM input formatter embedded + in the Amlogic AXG SoC family + +config SND_MESON_AXG_TDMOUT + tristate "Amlogic AXG TDM Output Support" + select SND_MESON_AXG_TDM_FORMATTER + select SND_MESON_AXG_TDM_INTERFACE + help + Select Y or M to add support for TDM output formatter embedded + in the Amlogic AXG SoC family + +config SND_MESON_AXG_SOUND_CARD + tristate "Amlogic AXG Sound Card Support" + select SND_MESON_AXG_TDM_INTERFACE + imply SND_MESON_AXG_FRDDR + imply SND_MESON_AXG_TODDR + imply SND_MESON_AXG_TDMIN + imply SND_MESON_AXG_TDMOUT + imply SND_MESON_AXG_SPDIFOUT + help + Select Y or M to add support for the AXG SoC sound card + +config SND_MESON_AXG_SPDIFOUT + tristate "Amlogic AXG SPDIF Output Support" + select SND_PCM_IEC958 + imply SND_SOC_SPDIF + help + Select Y or M to add support for SPDIF output serializer embedded + in the Amlogic AXG SoC family + +endmenu diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile new file mode 100644 index 000000000000..c5e003b093db --- /dev/null +++ b/sound/soc/meson/Makefile @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: (GPL-2.0 OR MIT) + +snd-soc-meson-axg-fifo-objs := axg-fifo.o +snd-soc-meson-axg-frddr-objs := axg-frddr.o +snd-soc-meson-axg-toddr-objs := axg-toddr.o +snd-soc-meson-axg-tdm-formatter-objs := axg-tdm-formatter.o +snd-soc-meson-axg-tdm-interface-objs := axg-tdm-interface.o +snd-soc-meson-axg-tdmin-objs := axg-tdmin.o +snd-soc-meson-axg-tdmout-objs := axg-tdmout.o +snd-soc-meson-axg-sound-card-objs := axg-card.o +snd-soc-meson-axg-spdifout-objs := axg-spdifout.o + +obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o +obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o +obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o +obj-$(CONFIG_SND_MESON_AXG_TDM_FORMATTER) += snd-soc-meson-axg-tdm-formatter.o +obj-$(CONFIG_SND_MESON_AXG_TDM_INTERFACE) += snd-soc-meson-axg-tdm-interface.o +obj-$(CONFIG_SND_MESON_AXG_TDMIN) += snd-soc-meson-axg-tdmin.o +obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o +obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o +obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c new file mode 100644 index 000000000000..2914ba0d965b --- /dev/null +++ b/sound/soc/meson/axg-card.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm.h" + +struct axg_card { + struct snd_soc_card card; + void **link_data; +}; + +struct axg_dai_link_tdm_mask { + u32 tx; + u32 rx; +}; + +struct axg_dai_link_tdm_data { + unsigned int mclk_fs; + unsigned int slots; + unsigned int slot_width; + u32 *tx_mask; + u32 *rx_mask; + struct axg_dai_link_tdm_mask *codec_masks; +}; + +#define PREFIX "amlogic," + +static int axg_card_reallocate_links(struct axg_card *priv, + unsigned int num_links) +{ + struct snd_soc_dai_link *links; + void **ldata; + + links = krealloc(priv->card.dai_link, + num_links * sizeof(*priv->card.dai_link), + GFP_KERNEL | __GFP_ZERO); + ldata = krealloc(priv->link_data, + num_links * sizeof(*priv->link_data), + GFP_KERNEL | __GFP_ZERO); + + if (!links || !ldata) { + dev_err(priv->card.dev, "failed to allocate links\n"); + return -ENOMEM; + } + + priv->card.dai_link = links; + priv->link_data = ldata; + priv->card.num_links = num_links; + return 0; +} + +static int axg_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name) +{ + struct of_phandle_args args; + int ret; + + if (!dai_name || !dai_of_node || !node) + return -EINVAL; + + ret = of_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(card->dev, "can't parse dai %d\n", ret); + return ret; + } + *dai_of_node = args.np; + + return snd_soc_get_dai_name(&args, dai_name); +} + +static int axg_card_set_link_name(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + const char *prefix) +{ + char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", + prefix, link->cpu_of_node->full_name); + if (!name) + return -ENOMEM; + + link->name = name; + link->stream_name = name; + + return 0; +} + +static void axg_card_clean_references(struct axg_card *priv) +{ + struct snd_soc_card *card = &priv->card; + struct snd_soc_dai_link *link; + int i, j; + + if (card->dai_link) { + for (i = 0; i < card->num_links; i++) { + link = &card->dai_link[i]; + of_node_put(link->cpu_of_node); + for (j = 0; j < link->num_codecs; j++) + of_node_put(link->codecs[j].of_node); + } + } + + if (card->aux_dev) { + for (i = 0; i < card->num_aux_devs; i++) + of_node_put(card->aux_dev[i].codec_of_node); + } + + kfree(card->dai_link); + kfree(priv->link_data); +} + +static int axg_card_add_aux_devices(struct snd_soc_card *card) +{ + struct device_node *node = card->dev->of_node; + struct snd_soc_aux_dev *aux; + int num, i; + + num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); + if (num == -ENOENT) { + /* + * It is ok to have no auxiliary devices but for this card it + * is a strange situtation. Let's warn the about it. + */ + dev_warn(card->dev, "card has no auxiliary devices\n"); + return 0; + } else if (num < 0) { + dev_err(card->dev, "error getting auxiliary devices: %d\n", + num); + return num; + } + + aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); + if (!aux) + return -ENOMEM; + card->aux_dev = aux; + card->num_aux_devs = num; + + for (i = 0; i < card->num_aux_devs; i++, aux++) { + aux->codec_of_node = + of_parse_phandle(node, "audio-aux-devs", i); + if (!aux->codec_of_node) + return -EINVAL; + } + + return 0; +} + +static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + struct snd_soc_dai *codec_dai; + unsigned int mclk; + int ret, i; + + if (be->mclk_fs) { + mclk = params_rate(params) * be->mclk_fs; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + return ret; + } + + return 0; +} + +static const struct snd_soc_ops axg_card_tdm_be_ops = { + .hw_params = axg_card_tdm_be_hw_params, +}; + +static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + struct snd_soc_dai *codec_dai; + int ret, i; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + ret = snd_soc_dai_set_tdm_slot(codec_dai, + be->codec_masks[i].tx, + be->codec_masks[i].rx, + be->slots, be->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(codec_dai->dev, + "setting tdm link slots failed\n"); + return ret; + } + } + + ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, be->tx_mask, be->rx_mask, + be->slots, be->slot_width); + if (ret) { + dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + return ret; + } + + return 0; +} + +static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + int ret; + + /* The loopback rx_mask is the pad tx_mask */ + ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, NULL, be->tx_mask, + be->slots, be->slot_width); + if (ret) { + dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + return ret; + } + + return 0; +} + +static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + int *index) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *pad = &card->dai_link[*index]; + struct snd_soc_dai_link *lb; + int ret; + + /* extend links */ + ret = axg_card_reallocate_links(priv, card->num_links + 1); + if (ret) + return ret; + + lb = &card->dai_link[*index + 1]; + + lb->name = kasprintf(GFP_KERNEL, "%s-lb", pad->name); + if (!lb->name) + return -ENOMEM; + + lb->stream_name = lb->name; + lb->cpu_of_node = pad->cpu_of_node; + lb->cpu_dai_name = "TDM Loopback"; + lb->codec_name = "snd-soc-dummy"; + lb->codec_dai_name = "snd-soc-dummy-dai"; + lb->dpcm_capture = 1; + lb->no_pcm = 1; + lb->ops = &axg_card_tdm_be_ops; + lb->init = axg_card_tdm_dai_lb_init; + + /* Provide the same link data to the loopback */ + priv->link_data[*index + 1] = priv->link_data[*index]; + + /* + * axg_card_clean_references() will iterate over this link, + * make sure the node count is balanced + */ + of_node_get(lb->cpu_of_node); + + /* Let add_links continue where it should */ + *index += 1; + + return 0; +} + +static unsigned int axg_card_parse_daifmt(struct device_node *node, + struct device_node *cpu_node) +{ + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, PREFIX, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + /* If no master is provided, default to cpu master */ + if (!bitclkmaster || bitclkmaster == cpu_node) { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; + } else { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; + } + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return daifmt; +} + +static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + struct axg_dai_link_tdm_data *be) +{ + char propname[32]; + u32 tx, rx; + int i; + + be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + sizeof(*be->tx_mask), GFP_KERNEL); + be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, + sizeof(*be->rx_mask), GFP_KERNEL); + if (!be->tx_mask || !be->rx_mask) + return -ENOMEM; + + for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) { + snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i); + snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]); + tx = max(tx, be->tx_mask[i]); + } + + /* Disable playback is the interface has no tx slots */ + if (!tx) + link->dpcm_playback = 0; + + for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) { + snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i); + snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]); + rx = max(rx, be->rx_mask[i]); + } + + /* Disable capture is the interface has no rx slots */ + if (!rx) + link->dpcm_capture = 0; + + /* ... but the interface should at least have one of them */ + if (!tx && !rx) { + dev_err(card->dev, "tdm link has no cpu slots\n"); + return -EINVAL; + } + + of_property_read_u32(node, "dai-tdm-slot-num", &be->slots); + if (!be->slots) { + /* + * If the slot number is not provided, set it such as it + * accommodates the largest mask + */ + be->slots = fls(max(tx, rx)); + } else if (be->slots < fls(max(tx, rx)) || be->slots > 32) { + /* + * Error if the slots can't accommodate the largest mask or + * if it is just too big + */ + dev_err(card->dev, "bad slot number\n"); + return -EINVAL; + } + + of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width); + + return 0; +} + +static int axg_card_parse_codecs_masks(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + struct axg_dai_link_tdm_data *be) +{ + struct axg_dai_link_tdm_mask *codec_mask; + struct device_node *np; + + codec_mask = devm_kcalloc(card->dev, link->num_codecs, + sizeof(*codec_mask), GFP_KERNEL); + if (!codec_mask) + return -ENOMEM; + + be->codec_masks = codec_mask; + + for_each_child_of_node(node, np) { + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", + &codec_mask->rx); + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", + &codec_mask->tx); + + codec_mask++; + } + + return 0; +} + +static int axg_card_parse_tdm(struct snd_soc_card *card, + struct device_node *node, + int *index) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct axg_dai_link_tdm_data *be; + int ret; + + /* Allocate tdm link parameters */ + be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; + + /* Setup tdm link */ + link->ops = &axg_card_tdm_be_ops; + link->init = axg_card_tdm_dai_init; + link->dai_fmt = axg_card_parse_daifmt(node, link->cpu_of_node); + + of_property_read_u32(node, "mclk-fs", &be->mclk_fs); + + ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); + if (ret) { + dev_err(card->dev, "error parsing tdm link slots\n"); + return ret; + } + + ret = axg_card_parse_codecs_masks(card, link, node, be); + if (ret) + return ret; + + /* Add loopback if the pad dai has playback */ + if (link->dpcm_playback) { + ret = axg_card_add_tdm_loopback(card, index); + if (ret) + return ret; + } + + return 0; +} + +static int axg_card_set_be_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node) +{ + struct snd_soc_dai_link_component *codec; + struct device_node *np; + int ret, num_codecs; + + link->no_pcm = 1; + link->dpcm_playback = 1; + link->dpcm_capture = 1; + + num_codecs = of_get_child_count(node); + if (!num_codecs) { + dev_err(card->dev, "be link %s has no codec\n", + node->full_name); + return -EINVAL; + } + + codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + link->codecs = codec; + link->num_codecs = num_codecs; + + for_each_child_of_node(node, np) { + ret = axg_card_parse_dai(card, np, &codec->of_node, + &codec->dai_name); + if (ret) { + of_node_put(np); + return ret; + } + + codec++; + } + + ret = axg_card_set_link_name(card, link, "be"); + if (ret) + dev_err(card->dev, "error setting %s link name\n", np->name); + + return ret; +} + +static int axg_card_set_fe_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + bool is_playback) +{ + link->dynamic = 1; + link->dpcm_merged_format = 1; + link->dpcm_merged_chan = 1; + link->dpcm_merged_rate = 1; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->codec_name = "snd-soc-dummy"; + + if (is_playback) + link->dpcm_playback = 1; + else + link->dpcm_capture = 1; + + return axg_card_set_link_name(card, link, "fe"); +} + +static int axg_card_cpu_is_capture_fe(struct device_node *np) +{ + return of_device_is_compatible(np, PREFIX "axg-toddr"); +} + +static int axg_card_cpu_is_playback_fe(struct device_node *np) +{ + return of_device_is_compatible(np, PREFIX "axg-frddr"); +} + +static int axg_card_cpu_is_tdm_iface(struct device_node *np) +{ + return of_device_is_compatible(np, PREFIX "axg-tdm-iface"); +} + +static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + int *index) +{ + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + int ret; + + ret = axg_card_parse_dai(card, np, &dai_link->cpu_of_node, + &dai_link->cpu_dai_name); + if (ret) + return ret; + + if (axg_card_cpu_is_playback_fe(dai_link->cpu_of_node)) + ret = axg_card_set_fe_link(card, dai_link, true); + else if (axg_card_cpu_is_capture_fe(dai_link->cpu_of_node)) + ret = axg_card_set_fe_link(card, dai_link, false); + else + ret = axg_card_set_be_link(card, dai_link, np); + + if (ret) + return ret; + + if (axg_card_cpu_is_tdm_iface(dai_link->cpu_of_node)) + ret = axg_card_parse_tdm(card, np, index); + + return ret; +} + +static int axg_card_add_links(struct snd_soc_card *card) +{ + struct axg_card *priv = snd_soc_card_get_drvdata(card); + struct device_node *node = card->dev->of_node; + struct device_node *np; + int num, i, ret; + + num = of_get_child_count(node); + if (!num) { + dev_err(card->dev, "card has no links\n"); + return -EINVAL; + } + + ret = axg_card_reallocate_links(priv, num); + if (ret) + return ret; + + i = 0; + for_each_child_of_node(node, np) { + ret = axg_card_add_link(card, np, &i); + if (ret) { + of_node_put(np); + return ret; + } + + i++; + } + + return 0; +} + +static int axg_card_parse_of_optional(struct snd_soc_card *card, + const char *propname, + int (*func)(struct snd_soc_card *c, + const char *p)) +{ + /* If property is not provided, don't fail ... */ + if (!of_property_read_bool(card->dev->of_node, propname)) + return 0; + + /* ... but do fail if it is provided and the parsing fails */ + return func(card, propname); +} + +static const struct of_device_id axg_card_of_match[] = { + { .compatible = "amlogic,axg-sound-card", }, + {} +}; +MODULE_DEVICE_TABLE(of, axg_card_of_match); + +static int axg_card_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axg_card *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + snd_soc_card_set_drvdata(&priv->card, priv); + + priv->card.owner = THIS_MODULE; + priv->card.dev = dev; + + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret < 0) + return ret; + + ret = axg_card_parse_of_optional(&priv->card, "audio-routing", + snd_soc_of_parse_audio_routing); + if (ret) { + dev_err(dev, "error while parsing routing\n"); + return ret; + } + + ret = axg_card_parse_of_optional(&priv->card, "audio-widgets", + snd_soc_of_parse_audio_simple_widgets); + if (ret) { + dev_err(dev, "error while parsing widgets\n"); + return ret; + } + + ret = axg_card_add_links(&priv->card); + if (ret) + goto out_err; + + ret = axg_card_add_aux_devices(&priv->card); + if (ret) + goto out_err; + + ret = devm_snd_soc_register_card(dev, &priv->card); + if (ret) + goto out_err; + + return 0; + +out_err: + axg_card_clean_references(priv); + return ret; +} + +static int axg_card_remove(struct platform_device *pdev) +{ + struct axg_card *priv = platform_get_drvdata(pdev); + + axg_card_clean_references(priv); + + return 0; +} + +static struct platform_driver axg_card_pdrv = { + .probe = axg_card_probe, + .remove = axg_card_remove, + .driver = { + .name = "axg-sound-card", + .of_match_table = axg_card_of_match, + }, +}; +module_platform_driver(axg_card_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c new file mode 100644 index 000000000000..30262550e37b --- /dev/null +++ b/sound/soc/meson/axg-fifo.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-fifo.h" + +/* + * This file implements the platform operations common to the playback and + * capture frontend DAI. The logic behind this two types of fifo is very + * similar but some difference exist. + * These differences the respective DAI drivers + */ + +static struct snd_pcm_hardware axg_fifo_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE), + + .formats = AXG_FIFO_FORMATS, + .rate_min = 5512, + .rate_max = 192000, + .channels_min = 1, + .channels_max = AXG_FIFO_CH_MAX, + .period_bytes_min = AXG_FIFO_MIN_DEPTH, + .period_bytes_max = UINT_MAX, + .periods_min = 2, + .periods_max = UINT_MAX, + + /* No real justification for this */ + .buffer_bytes_max = 1 * 1024 * 1024, +}; + +static struct snd_soc_dai *axg_fifo_dai(struct snd_pcm_substream *ss) +{ + struct snd_soc_pcm_runtime *rtd = ss->private_data; + + return rtd->cpu_dai; +} + +static struct axg_fifo *axg_fifo_data(struct snd_pcm_substream *ss) +{ + struct snd_soc_dai *dai = axg_fifo_dai(ss); + + return snd_soc_dai_get_drvdata(dai); +} + +static struct device *axg_fifo_dev(struct snd_pcm_substream *ss) +{ + struct snd_soc_dai *dai = axg_fifo_dai(ss); + + return dai->dev; +} + +static void __dma_enable(struct axg_fifo *fifo, bool enable) +{ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_DMA_EN, + enable ? CTRL0_DMA_EN : 0); +} + +static int axg_fifo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + __dma_enable(fifo, true); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + __dma_enable(fifo, false); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + struct snd_pcm_runtime *runtime = ss->runtime; + unsigned int addr; + + regmap_read(fifo->map, FIFO_STATUS2, &addr); + + return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr); +} + +static int axg_fifo_pcm_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = ss->runtime; + struct axg_fifo *fifo = axg_fifo_data(ss); + dma_addr_t end_ptr; + unsigned int burst_num; + int ret; + + ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + /* Setup dma memory pointers */ + end_ptr = runtime->dma_addr + runtime->dma_bytes - AXG_FIFO_BURST; + regmap_write(fifo->map, FIFO_START_ADDR, runtime->dma_addr); + regmap_write(fifo->map, FIFO_FINISH_ADDR, end_ptr); + + /* Setup interrupt periodicity */ + burst_num = params_period_bytes(params) / AXG_FIFO_BURST; + regmap_write(fifo->map, FIFO_INT_ADDR, burst_num); + + /* Enable block count irq */ + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), + CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT)); + + return 0; +} + +static int axg_fifo_pcm_hw_free(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + + /* Disable the block count irq */ + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), 0); + + return snd_pcm_lib_free_pages(ss); +} + +static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask) +{ + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_INT_CLR(FIFO_INT_MASK), + CTRL1_INT_CLR(mask)); + + /* Clear must also be cleared */ + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_INT_CLR(FIFO_INT_MASK), + 0); +} + +static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) +{ + struct snd_pcm_substream *ss = dev_id; + struct axg_fifo *fifo = axg_fifo_data(ss); + unsigned int status; + + regmap_read(fifo->map, FIFO_STATUS1, &status); + + status = STATUS1_INT_STS(status) & FIFO_INT_MASK; + if (status & FIFO_INT_COUNT_REPEAT) + snd_pcm_period_elapsed(ss); + else + dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n", + status); + + /* Ack irqs */ + axg_fifo_ack_irq(fifo, status); + + return IRQ_RETVAL(status); +} + +static int axg_fifo_pcm_open(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + struct device *dev = axg_fifo_dev(ss); + int ret; + + snd_soc_set_runtime_hwparams(ss, &axg_fifo_hw); + + /* + * Make sure the buffer and period size are multiple of the FIFO + * minimum depth size + */ + ret = snd_pcm_hw_constraint_step(ss->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + AXG_FIFO_MIN_DEPTH); + if (ret) + return ret; + + ret = snd_pcm_hw_constraint_step(ss->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + AXG_FIFO_MIN_DEPTH); + if (ret) + return ret; + + ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0, + dev_name(dev), ss); + + /* Enable pclk to access registers and clock the fifo ip */ + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + /* Setup status2 so it reports the memory pointer */ + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_STATUS2_SEL_MASK, + CTRL1_STATUS2_SEL(STATUS2_SEL_DDR_READ)); + + /* Make sure the dma is initially disabled */ + __dma_enable(fifo, false); + + /* Disable irqs until params are ready */ + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_INT_EN(FIFO_INT_MASK), 0); + + /* Clear any pending interrupt */ + axg_fifo_ack_irq(fifo, FIFO_INT_MASK); + + /* Take memory arbitror out of reset */ + ret = reset_control_deassert(fifo->arb); + if (ret) + clk_disable_unprepare(fifo->pclk); + + return ret; +} + +static int axg_fifo_pcm_close(struct snd_pcm_substream *ss) +{ + struct axg_fifo *fifo = axg_fifo_data(ss); + int ret; + + /* Put the memory arbitror back in reset */ + ret = reset_control_assert(fifo->arb); + + /* Disable fifo ip and register access */ + clk_disable_unprepare(fifo->pclk); + + /* remove IRQ */ + free_irq(fifo->irq, ss); + + return ret; +} + +const struct snd_pcm_ops axg_fifo_pcm_ops = { + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = axg_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, +}; +EXPORT_SYMBOL_GPL(axg_fifo_pcm_ops); + +int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type) +{ + struct snd_card *card = rtd->card->snd_card; + size_t size = axg_fifo_hw.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages(rtd->pcm->streams[type].substream, + SNDRV_DMA_TYPE_DEV, card->dev, + size, size); +} +EXPORT_SYMBOL_GPL(axg_fifo_pcm_new); + +static const struct regmap_config axg_fifo_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = FIFO_STATUS2, +}; + +int axg_fifo_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct axg_fifo_match_data *data; + struct axg_fifo *fifo; + struct resource *res; + void __iomem *regs; + + data = of_device_get_match_data(dev); + if (!data) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); + if (!fifo) + return -ENOMEM; + platform_set_drvdata(pdev, fifo); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + fifo->map = devm_regmap_init_mmio(dev, regs, &axg_fifo_regmap_cfg); + if (IS_ERR(fifo->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(fifo->map)); + return PTR_ERR(fifo->map); + } + + fifo->pclk = devm_clk_get(dev, NULL); + if (IS_ERR(fifo->pclk)) { + if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %ld\n", + PTR_ERR(fifo->pclk)); + return PTR_ERR(fifo->pclk); + } + + fifo->arb = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(fifo->arb)) { + if (PTR_ERR(fifo->arb) != -EPROBE_DEFER) + dev_err(dev, "failed to get arb reset: %ld\n", + PTR_ERR(fifo->arb)); + return PTR_ERR(fifo->arb); + } + + fifo->irq = of_irq_get(dev->of_node, 0); + if (fifo->irq <= 0) { + dev_err(dev, "failed to get irq: %d\n", fifo->irq); + return fifo->irq; + } + + return devm_snd_soc_register_component(dev, data->component_drv, + data->dai_drv, 1); +} +EXPORT_SYMBOL_GPL(axg_fifo_probe); + +MODULE_DESCRIPTION("Amlogic AXG fifo driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h new file mode 100644 index 000000000000..cb6c4013ca33 --- /dev/null +++ b/sound/soc/meson/axg-fifo.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AXG_FIFO_H +#define _MESON_AXG_FIFO_H + +struct clk; +struct platform_device; +struct regmap; +struct reset_control; + +struct snd_soc_component_driver; +struct snd_soc_dai; +struct snd_soc_dai_driver; +struct snd_pcm_ops; +struct snd_soc_pcm_runtime; + +#define AXG_FIFO_CH_MAX 128 +#define AXG_FIFO_RATES (SNDRV_PCM_RATE_5512 | \ + SNDRV_PCM_RATE_8000_192000) +#define AXG_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AXG_FIFO_BURST 8 +#define AXG_FIFO_MIN_CNT 64 +#define AXG_FIFO_MIN_DEPTH (AXG_FIFO_BURST * AXG_FIFO_MIN_CNT) + +#define FIFO_INT_ADDR_FINISH BIT(0) +#define FIFO_INT_ADDR_INT BIT(1) +#define FIFO_INT_COUNT_REPEAT BIT(2) +#define FIFO_INT_COUNT_ONCE BIT(3) +#define FIFO_INT_FIFO_ZERO BIT(4) +#define FIFO_INT_FIFO_DEPTH BIT(5) +#define FIFO_INT_MASK GENMASK(7, 0) + +#define FIFO_CTRL0 0x00 +#define CTRL0_DMA_EN BIT(31) +#define CTRL0_INT_EN(x) ((x) << 16) +#define CTRL0_SEL_MASK GENMASK(2, 0) +#define CTRL0_SEL_SHIFT 0 +#define FIFO_CTRL1 0x04 +#define CTRL1_INT_CLR(x) ((x) << 0) +#define CTRL1_STATUS2_SEL_MASK GENMASK(11, 8) +#define CTRL1_STATUS2_SEL(x) ((x) << 8) +#define STATUS2_SEL_DDR_READ 0 +#define CTRL1_THRESHOLD_MASK GENMASK(23, 16) +#define CTRL1_THRESHOLD(x) ((x) << 16) +#define CTRL1_FRDDR_DEPTH_MASK GENMASK(31, 24) +#define CTRL1_FRDDR_DEPTH(x) ((x) << 24) +#define FIFO_START_ADDR 0x08 +#define FIFO_FINISH_ADDR 0x0c +#define FIFO_INT_ADDR 0x10 +#define FIFO_STATUS1 0x14 +#define STATUS1_INT_STS(x) ((x) << 0) +#define FIFO_STATUS2 0x18 + +struct axg_fifo { + struct regmap *map; + struct clk *pclk; + struct reset_control *arb; + int irq; +}; + +struct axg_fifo_match_data { + const struct snd_soc_component_driver *component_drv; + struct snd_soc_dai_driver *dai_drv; +}; + +extern const struct snd_pcm_ops axg_fifo_pcm_ops; + +int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type); +int axg_fifo_probe(struct platform_device *pdev); + +#endif /* _MESON_AXG_FIFO_H */ diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c new file mode 100644 index 000000000000..a6f6f6a2eca8 --- /dev/null +++ b/sound/soc/meson/axg-frddr.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +/* This driver implements the frontend playback DAI of AXG based SoCs */ + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-fifo.h" + +#define CTRL0_FRDDR_PP_MODE BIT(30) + +static int axg_frddr_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + unsigned int fifo_depth, fifo_threshold; + int ret; + + /* Enable pclk to access registers and clock the fifo ip */ + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + /* Apply single buffer mode to the interface */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_FRDDR_PP_MODE, 0); + + /* + * TODO: We could adapt the fifo depth and the fifo threshold + * depending on the expected memory throughput and lantencies + * For now, we'll just use the same values as the vendor kernel + * Depth and threshold are zero based. + */ + fifo_depth = AXG_FIFO_MIN_CNT - 1; + fifo_threshold = (AXG_FIFO_MIN_CNT / 2) - 1; + regmap_update_bits(fifo->map, FIFO_CTRL1, + CTRL1_FRDDR_DEPTH_MASK | CTRL1_THRESHOLD_MASK, + CTRL1_FRDDR_DEPTH(fifo_depth) | + CTRL1_THRESHOLD(fifo_threshold)); + + return 0; +} + +static void axg_frddr_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(fifo->pclk); +} + +static int axg_frddr_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_PLAYBACK); +} + +static const struct snd_soc_dai_ops axg_frddr_ops = { + .startup = axg_frddr_dai_startup, + .shutdown = axg_frddr_dai_shutdown, +}; + +static struct snd_soc_dai_driver axg_frddr_dai_drv = { + .name = "FRDDR", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = AXG_FIFO_CH_MAX, + .rates = AXG_FIFO_RATES, + .formats = AXG_FIFO_FORMATS, + }, + .ops = &axg_frddr_ops, + .pcm_new = axg_frddr_pcm_new, +}; + +static const char * const axg_frddr_sel_texts[] = { + "OUT 0", "OUT 1", "OUT 2", "OUT 3" +}; + +static SOC_ENUM_SINGLE_DECL(axg_frddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT, + axg_frddr_sel_texts); + +static const struct snd_kcontrol_new axg_frddr_out_demux = + SOC_DAPM_ENUM("Output Sink", axg_frddr_sel_enum); + +static const struct snd_soc_dapm_widget axg_frddr_dapm_widgets[] = { + SND_SOC_DAPM_DEMUX("SINK SEL", SND_SOC_NOPM, 0, 0, + &axg_frddr_out_demux), + SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_frddr_dapm_routes[] = { + { "SINK SEL", NULL, "Playback" }, + { "OUT 0", "OUT 0", "SINK SEL" }, + { "OUT 1", "OUT 1", "SINK SEL" }, + { "OUT 2", "OUT 2", "SINK SEL" }, + { "OUT 3", "OUT 3", "SINK SEL" }, +}; + +static const struct snd_soc_component_driver axg_frddr_component_drv = { + .dapm_widgets = axg_frddr_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets), + .dapm_routes = axg_frddr_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes), + .ops = &axg_fifo_pcm_ops +}; + +static const struct axg_fifo_match_data axg_frddr_match_data = { + .component_drv = &axg_frddr_component_drv, + .dai_drv = &axg_frddr_dai_drv +}; + +static const struct of_device_id axg_frddr_of_match[] = { + { + .compatible = "amlogic,axg-frddr", + .data = &axg_frddr_match_data, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_frddr_of_match); + +static struct platform_driver axg_frddr_pdrv = { + .probe = axg_fifo_probe, + .driver = { + .name = "axg-frddr", + .of_match_table = axg_frddr_of_match, + }, +}; +module_platform_driver(axg_frddr_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG playback fifo driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c new file mode 100644 index 000000000000..9dea528053ad --- /dev/null +++ b/sound/soc/meson/axg-spdifout.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/pcm_params.h> +#include <sound/pcm_iec958.h> + +/* + * NOTE: + * The meaning of bits SPDIFOUT_CTRL0_XXX_SEL is actually the opposite + * of what the documentation says. Manual control on V, U and C bits is + * applied when the related sel bits are cleared + */ + +#define SPDIFOUT_STAT 0x00 +#define SPDIFOUT_GAIN0 0x04 +#define SPDIFOUT_GAIN1 0x08 +#define SPDIFOUT_CTRL0 0x0c +#define SPDIFOUT_CTRL0_EN BIT(31) +#define SPDIFOUT_CTRL0_RST_OUT BIT(29) +#define SPDIFOUT_CTRL0_RST_IN BIT(28) +#define SPDIFOUT_CTRL0_USEL BIT(26) +#define SPDIFOUT_CTRL0_USET BIT(25) +#define SPDIFOUT_CTRL0_CHSTS_SEL BIT(24) +#define SPDIFOUT_CTRL0_DATA_SEL BIT(20) +#define SPDIFOUT_CTRL0_MSB_FIRST BIT(19) +#define SPDIFOUT_CTRL0_VSEL BIT(18) +#define SPDIFOUT_CTRL0_VSET BIT(17) +#define SPDIFOUT_CTRL0_MASK_MASK GENMASK(11, 4) +#define SPDIFOUT_CTRL0_MASK(x) ((x) << 4) +#define SPDIFOUT_CTRL1 0x10 +#define SPDIFOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8) +#define SPDIFOUT_CTRL1_MSB_POS(x) ((x) << 8) +#define SPDIFOUT_CTRL1_TYPE_MASK GENMASK(6, 4) +#define SPDIFOUT_CTRL1_TYPE(x) ((x) << 4) +#define SPDIFOUT_PREAMB 0x14 +#define SPDIFOUT_SWAP 0x18 +#define SPDIFOUT_CHSTS0 0x1c +#define SPDIFOUT_CHSTS1 0x20 +#define SPDIFOUT_CHSTS2 0x24 +#define SPDIFOUT_CHSTS3 0x28 +#define SPDIFOUT_CHSTS4 0x2c +#define SPDIFOUT_CHSTS5 0x30 +#define SPDIFOUT_CHSTS6 0x34 +#define SPDIFOUT_CHSTS7 0x38 +#define SPDIFOUT_CHSTS8 0x3c +#define SPDIFOUT_CHSTS9 0x40 +#define SPDIFOUT_CHSTSA 0x44 +#define SPDIFOUT_CHSTSB 0x48 +#define SPDIFOUT_MUTE_VAL 0x4c + +struct axg_spdifout { + struct regmap *map; + struct clk *mclk; + struct clk *pclk; +}; + +static void axg_spdifout_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_RST_OUT | SPDIFOUT_CTRL0_RST_IN, + 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_RST_OUT, SPDIFOUT_CTRL0_RST_OUT); + regmap_update_bits(map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_RST_IN, SPDIFOUT_CTRL0_RST_IN); + + /* Enable spdifout */ + regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN, + SPDIFOUT_CTRL0_EN); +} + +static void axg_spdifout_disable(struct regmap *map) +{ + regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN, 0); +} + +static int axg_spdifout_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + axg_spdifout_enable(priv->map); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + axg_spdifout_disable(priv->map); + return 0; + + default: + return -EINVAL; + } +} + +static int axg_spdifout_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + + /* Use spdif valid bit to perform digital mute */ + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_VSET, + mute ? SPDIFOUT_CTRL0_VSET : 0); + + return 0; +} + +static int axg_spdifout_sample_fmt(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + unsigned int val; + + /* Set the samples spdifout will pull from the FIFO */ + switch (params_channels(params)) { + case 1: + val = SPDIFOUT_CTRL0_MASK(0x1); + break; + case 2: + val = SPDIFOUT_CTRL0_MASK(0x3); + break; + default: + dev_err(dai->dev, "too many channels for spdif dai: %u\n", + params_channels(params)); + return -EINVAL; + } + + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_MASK_MASK, val); + + /* FIFO data are arranged in chunks of 64bits */ + switch (params_physical_width(params)) { + case 8: + /* 8 samples of 8 bits */ + val = SPDIFOUT_CTRL1_TYPE(0); + break; + case 16: + /* 4 samples of 16 bits - right justified */ + val = SPDIFOUT_CTRL1_TYPE(2); + break; + case 32: + /* 2 samples of 32 bits - right justified */ + val = SPDIFOUT_CTRL1_TYPE(4); + break; + default: + dev_err(dai->dev, "Unsupported physical width: %u\n", + params_physical_width(params)); + return -EINVAL; + } + + /* Position of the MSB in FIFO samples */ + val |= SPDIFOUT_CTRL1_MSB_POS(params_width(params) - 1); + + regmap_update_bits(priv->map, SPDIFOUT_CTRL1, + SPDIFOUT_CTRL1_MSB_POS_MASK | + SPDIFOUT_CTRL1_TYPE_MASK, val); + + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL, + 0); + + return 0; +} + +static int axg_spdifout_set_chsts(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + unsigned int offset; + int ret; + u8 cs[4]; + u32 val; + + ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, 4); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + val = cs[0] | cs[1] << 8 | cs[2] << 16 | cs[3] << 24; + + /* Setup channel status A bits [31 - 0]*/ + regmap_write(priv->map, SPDIFOUT_CHSTS0, val); + + /* Clear channel status A bits [191 - 32] */ + for (offset = SPDIFOUT_CHSTS1; offset <= SPDIFOUT_CHSTS5; + offset += regmap_get_reg_stride(priv->map)) + regmap_write(priv->map, offset, 0); + + /* Setup channel status B bits [31 - 0]*/ + regmap_write(priv->map, SPDIFOUT_CHSTS6, val); + + /* Clear channel status B bits [191 - 32] */ + for (offset = SPDIFOUT_CHSTS7; offset <= SPDIFOUT_CHSTSB; + offset += regmap_get_reg_stride(priv->map)) + regmap_write(priv->map, offset, 0); + + return 0; +} + +static int axg_spdifout_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + int ret; + + /* 2 * 32bits per subframe * 2 channels = 128 */ + ret = clk_set_rate(priv->mclk, rate * 128); + if (ret) { + dev_err(dai->dev, "failed to set spdif clock\n"); + return ret; + } + + ret = axg_spdifout_sample_fmt(params, dai); + if (ret) { + dev_err(dai->dev, "failed to setup sample format\n"); + return ret; + } + + ret = axg_spdifout_set_chsts(params, dai); + if (ret) { + dev_err(dai->dev, "failed to setup channel status words\n"); + return ret; + } + + return 0; +} + +static int axg_spdifout_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + /* Clock the spdif output block */ + ret = clk_prepare_enable(priv->pclk); + if (ret) { + dev_err(dai->dev, "failed to enable pclk\n"); + return ret; + } + + /* Make sure the block is initially stopped */ + axg_spdifout_disable(priv->map); + + /* Insert data from bit 27 lsb first */ + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL, + 0); + + /* Manual control of V, C and U, U = 0 */ + regmap_update_bits(priv->map, SPDIFOUT_CTRL0, + SPDIFOUT_CTRL0_CHSTS_SEL | SPDIFOUT_CTRL0_VSEL | + SPDIFOUT_CTRL0_USEL | SPDIFOUT_CTRL0_USET, + 0); + + /* Static SWAP configuration ATM */ + regmap_write(priv->map, SPDIFOUT_SWAP, 0x10); + + return 0; +} + +static void axg_spdifout_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(priv->pclk); +} + +static const struct snd_soc_dai_ops axg_spdifout_ops = { + .trigger = axg_spdifout_trigger, + .digital_mute = axg_spdifout_digital_mute, + .hw_params = axg_spdifout_hw_params, + .startup = axg_spdifout_startup, + .shutdown = axg_spdifout_shutdown, +}; + +static struct snd_soc_dai_driver axg_spdifout_dai_drv[] = { + { + .name = "SPDIF Output", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), + .formats = (SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &axg_spdifout_ops, + }, +}; + +static const char * const spdifout_sel_texts[] = { + "IN 0", "IN 1", "IN 2", +}; + +static SOC_ENUM_SINGLE_DECL(axg_spdifout_sel_enum, SPDIFOUT_CTRL1, 24, + spdifout_sel_texts); + +static const struct snd_kcontrol_new axg_spdifout_in_mux = + SOC_DAPM_ENUM("Input Source", axg_spdifout_sel_enum); + +static const struct snd_soc_dapm_widget axg_spdifout_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_spdifout_in_mux), +}; + +static const struct snd_soc_dapm_route axg_spdifout_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "Playback", NULL, "SRC SEL" }, +}; + +static const struct snd_kcontrol_new axg_spdifout_controls[] = { + SOC_DOUBLE("Playback Volume", SPDIFOUT_GAIN0, 0, 8, 255, 0), + SOC_DOUBLE("Playback Switch", SPDIFOUT_CTRL0, 22, 21, 1, 1), + SOC_SINGLE("Playback Gain Enable Switch", + SPDIFOUT_CTRL1, 26, 1, 0), + SOC_SINGLE("Playback Channels Mix Switch", + SPDIFOUT_CTRL0, 23, 1, 0), +}; + +static int axg_spdifout_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct axg_spdifout *priv = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level now = + snd_soc_component_get_bias_level(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (now == SND_SOC_BIAS_STANDBY) + ret = clk_prepare_enable(priv->mclk); + break; + + case SND_SOC_BIAS_STANDBY: + if (now == SND_SOC_BIAS_PREPARE) + clk_disable_unprepare(priv->mclk); + break; + + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_ON: + break; + } + + return ret; +} + +static const struct snd_soc_component_driver axg_spdifout_component_drv = { + .controls = axg_spdifout_controls, + .num_controls = ARRAY_SIZE(axg_spdifout_controls), + .dapm_widgets = axg_spdifout_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_spdifout_dapm_widgets), + .dapm_routes = axg_spdifout_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_spdifout_dapm_routes), + .set_bias_level = axg_spdifout_set_bias_level, +}; + +static const struct regmap_config axg_spdifout_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPDIFOUT_MUTE_VAL, +}; + +static const struct of_device_id axg_spdifout_of_match[] = { + { .compatible = "amlogic,axg-spdifout", }, + {} +}; +MODULE_DEVICE_TABLE(of, axg_spdifout_of_match); + +static int axg_spdifout_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axg_spdifout *priv; + struct resource *res; + void __iomem *regs; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifout_regmap_cfg); + if (IS_ERR(priv->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(priv->map)); + return PTR_ERR(priv->map); + } + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk)) { + ret = PTR_ERR(priv->mclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get mclk: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv, + axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv)); +} + +static struct platform_driver axg_spdifout_pdrv = { + .probe = axg_spdifout_probe, + .driver = { + .name = "axg-spdifout", + .of_match_table = axg_spdifout_of_match, + }, +}; +module_platform_driver(axg_spdifout_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG SPDIF Output driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c new file mode 100644 index 000000000000..43e390f9358a --- /dev/null +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "axg-tdm-formatter.h" + +struct axg_tdm_formatter { + struct list_head list; + struct axg_tdm_stream *stream; + const struct axg_tdm_formatter_driver *drv; + struct clk *pclk; + struct clk *sclk; + struct clk *lrclk; + struct clk *sclk_sel; + struct clk *lrclk_sel; + bool enabled; + struct regmap *map; +}; + +int axg_tdm_formatter_set_channel_masks(struct regmap *map, + struct axg_tdm_stream *ts, + unsigned int offset) +{ + unsigned int val, ch = ts->channels; + unsigned long mask; + int i, j; + + /* + * Distribute the channels of the stream over the available slots + * of each TDM lane + */ + for (i = 0; i < AXG_TDM_NUM_LANES; i++) { + val = 0; + mask = ts->mask[i]; + + for (j = find_first_bit(&mask, 32); + (j < 32) && ch; + j = find_next_bit(&mask, 32, j + 1)) { + val |= 1 << j; + ch -= 1; + } + + regmap_write(map, offset, val); + offset += regmap_get_reg_stride(map); + } + + /* + * If we still have channel left at the end of the process, it means + * the stream has more channels than we can accommodate and we should + * have caught this earlier. + */ + if (WARN_ON(ch != 0)) { + pr_err("channel mask error\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks); + +static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter) +{ + struct axg_tdm_stream *ts = formatter->stream; + bool invert = formatter->drv->invert_sclk; + int ret; + + /* Do nothing if the formatter is already enabled */ + if (formatter->enabled) + return 0; + + /* + * If sclk is inverted, invert it back and provide the inversion + * required by the formatter + */ + invert ^= axg_tdm_sclk_invert(ts->iface->fmt); + ret = clk_set_phase(formatter->sclk, invert ? 180 : 0); + if (ret) + return ret; + + /* Setup the stream parameter in the formatter */ + ret = formatter->drv->ops->prepare(formatter->map, formatter->stream); + if (ret) + return ret; + + /* Enable the signal clocks feeding the formatter */ + ret = clk_prepare_enable(formatter->sclk); + if (ret) + return ret; + + ret = clk_prepare_enable(formatter->lrclk); + if (ret) { + clk_disable_unprepare(formatter->sclk); + return ret; + } + + /* Finally, actually enable the formatter */ + formatter->drv->ops->enable(formatter->map); + formatter->enabled = true; + + return 0; +} + +static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter) +{ + /* Do nothing if the formatter is already disabled */ + if (!formatter->enabled) + return; + + formatter->drv->ops->disable(formatter->map); + clk_disable_unprepare(formatter->lrclk); + clk_disable_unprepare(formatter->sclk); + formatter->enabled = false; +} + +static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter) +{ + struct axg_tdm_stream *ts = formatter->stream; + int ret = 0; + + mutex_lock(&ts->lock); + + /* Catch up if the stream is already running when we attach */ + if (ts->ready) { + ret = axg_tdm_formatter_enable(formatter); + if (ret) { + pr_err("failed to enable formatter\n"); + goto out; + } + } + + list_add_tail(&formatter->list, &ts->formatter_list); +out: + mutex_unlock(&ts->lock); + return ret; +} + +static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter) +{ + struct axg_tdm_stream *ts = formatter->stream; + + mutex_lock(&ts->lock); + list_del(&formatter->list); + mutex_unlock(&ts->lock); + + axg_tdm_formatter_disable(formatter); +} + +static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter, + struct snd_soc_dapm_widget *w) +{ + struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w); + int ret; + + /* + * If we don't get a stream at this stage, it would mean that the + * widget is powering up but is not attached to any backend DAI. + * It should not happen, ever ! + */ + if (WARN_ON(!ts)) + return -ENODEV; + + /* Clock our device */ + ret = clk_prepare_enable(formatter->pclk); + if (ret) + return ret; + + /* Reparent the bit clock to the TDM interface */ + ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk); + if (ret) + goto disable_pclk; + + /* Reparent the sample clock to the TDM interface */ + ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk); + if (ret) + goto disable_pclk; + + formatter->stream = ts; + ret = axg_tdm_formatter_attach(formatter); + if (ret) + goto disable_pclk; + + return 0; + +disable_pclk: + clk_disable_unprepare(formatter->pclk); + return ret; +} + +static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter) +{ + axg_tdm_formatter_dettach(formatter); + clk_disable_unprepare(formatter->pclk); + formatter->stream = NULL; +} + +int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = axg_tdm_formatter_power_up(formatter, w); + break; + + case SND_SOC_DAPM_PRE_PMD: + axg_tdm_formatter_power_down(formatter); + break; + + default: + dev_err(c->dev, "Unexpected event %d\n", event); + return -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(axg_tdm_formatter_event); + +int axg_tdm_formatter_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct axg_tdm_formatter_driver *drv; + struct axg_tdm_formatter *formatter; + struct resource *res; + void __iomem *regs; + int ret; + + drv = of_device_get_match_data(dev); + if (!drv) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return -ENOMEM; + platform_set_drvdata(pdev, formatter); + formatter->drv = drv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); + if (IS_ERR(formatter->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(formatter->map)); + return PTR_ERR(formatter->map); + } + + /* Peripharal clock */ + formatter->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(formatter->pclk)) { + ret = PTR_ERR(formatter->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + /* Formatter bit clock */ + formatter->sclk = devm_clk_get(dev, "sclk"); + if (IS_ERR(formatter->sclk)) { + ret = PTR_ERR(formatter->sclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get sclk: %d\n", ret); + return ret; + } + + /* Formatter sample clock */ + formatter->lrclk = devm_clk_get(dev, "lrclk"); + if (IS_ERR(formatter->lrclk)) { + ret = PTR_ERR(formatter->lrclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get lrclk: %d\n", ret); + return ret; + } + + /* Formatter bit clock input multiplexer */ + formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); + if (IS_ERR(formatter->sclk_sel)) { + ret = PTR_ERR(formatter->sclk_sel); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get sclk_sel: %d\n", ret); + return ret; + } + + /* Formatter sample clock input multiplexer */ + formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); + if (IS_ERR(formatter->lrclk_sel)) { + ret = PTR_ERR(formatter->lrclk_sel); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get lrclk_sel: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(dev, drv->component_drv, + NULL, 0); +} +EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe); + +int axg_tdm_stream_start(struct axg_tdm_stream *ts) +{ + struct axg_tdm_formatter *formatter; + int ret = 0; + + mutex_lock(&ts->lock); + ts->ready = true; + + /* Start all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + ret = axg_tdm_formatter_enable(formatter); + if (ret) { + pr_err("failed to start tdm stream\n"); + goto out; + } + } + +out: + mutex_unlock(&ts->lock); + return ret; +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_start); + +void axg_tdm_stream_stop(struct axg_tdm_stream *ts) +{ + struct axg_tdm_formatter *formatter; + + mutex_lock(&ts->lock); + ts->ready = false; + + /* Stop all the formatters attached to the stream */ + list_for_each_entry(formatter, &ts->formatter_list, list) { + axg_tdm_formatter_disable(formatter); + } + + mutex_unlock(&ts->lock); +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_stop); + +struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface) +{ + struct axg_tdm_stream *ts; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts) { + INIT_LIST_HEAD(&ts->formatter_list); + mutex_init(&ts->lock); + ts->iface = iface; + } + + return ts; +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc); + +void axg_tdm_stream_free(struct axg_tdm_stream *ts) +{ + /* + * If the list is not empty, it would mean that one of the formatter + * widget is still powered and attached to the interface while we + * we are removing the TDM DAI. It should not be possible + */ + WARN_ON(!list_empty(&ts->formatter_list)); + mutex_destroy(&ts->lock); + kfree(ts); +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_free); + +MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm-formatter.h b/sound/soc/meson/axg-tdm-formatter.h new file mode 100644 index 000000000000..cf947caf3cb1 --- /dev/null +++ b/sound/soc/meson/axg-tdm-formatter.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) + * + * Copyright (c) 2018 Baylibre SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AXG_TDM_FORMATTER_H +#define _MESON_AXG_TDM_FORMATTER_H + +#include "axg-tdm.h" + +struct platform_device; +struct regmap; +struct snd_soc_dapm_widget; +struct snd_kcontrol; + +struct axg_tdm_formatter_ops { + struct axg_tdm_stream *(*get_stream)(struct snd_soc_dapm_widget *w); + void (*enable)(struct regmap *map); + void (*disable)(struct regmap *map); + int (*prepare)(struct regmap *map, struct axg_tdm_stream *ts); +}; + +struct axg_tdm_formatter_driver { + const struct snd_soc_component_driver *component_drv; + const struct regmap_config *regmap_cfg; + const struct axg_tdm_formatter_ops *ops; + bool invert_sclk; +}; + +int axg_tdm_formatter_set_channel_masks(struct regmap *map, + struct axg_tdm_stream *ts, + unsigned int offset); +int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, + int event); +int axg_tdm_formatter_probe(struct platform_device *pdev); + +#endif /* _MESON_AXG_TDM_FORMATTER_H */ diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c new file mode 100644 index 000000000000..7b8baf46d968 --- /dev/null +++ b/sound/soc/meson/axg-tdm-interface.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm.h" + +enum { + TDM_IFACE_PAD, + TDM_IFACE_LOOPBACK, +}; + +static unsigned int axg_tdm_slots_total(u32 *mask) +{ + unsigned int slots = 0; + int i; + + if (!mask) + return 0; + + /* Count the total number of slots provided by all 4 lanes */ + for (i = 0; i < AXG_TDM_NUM_LANES; i++) + slots += hweight32(mask[i]); + + return slots; +} + +int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, + u32 *rx_mask, unsigned int slots, + unsigned int slot_width) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *tx = (struct axg_tdm_stream *) + dai->playback_dma_data; + struct axg_tdm_stream *rx = (struct axg_tdm_stream *) + dai->capture_dma_data; + unsigned int tx_slots, rx_slots; + + tx_slots = axg_tdm_slots_total(tx_mask); + rx_slots = axg_tdm_slots_total(rx_mask); + + /* We should at least have a slot for a valid interface */ + if (!tx_slots && !rx_slots) { + dev_err(dai->dev, "interface has no slot\n"); + return -EINVAL; + } + + /* + * Amend the dai driver channel number and let dpcm channel merge do + * its job + */ + if (tx) { + tx->mask = tx_mask; + dai->driver->playback.channels_max = tx_slots; + } + + if (rx) { + rx->mask = rx_mask; + dai->driver->capture.channels_max = rx_slots; + } + + iface->slots = slots; + + switch (slot_width) { + case 0: + /* defaults width to 32 if not provided */ + iface->slot_width = 32; + break; + case 8: + case 16: + case 24: + case 32: + iface->slot_width = slot_width; + break; + default: + dev_err(dai->dev, "unsupported slot width: %d\n", slot_width); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots); + +static int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + int ret = -ENOTSUPP; + + if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) { + if (!iface->mclk) { + dev_warn(dai->dev, "master clock not provided\n"); + } else { + ret = clk_set_rate(iface->mclk, freq); + if (!ret) + iface->mclk_rate = freq; + } + } + + return ret; +} + +static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + + /* These modes are not supported */ + if (fmt & (SND_SOC_DAIFMT_CBS_CFM | SND_SOC_DAIFMT_CBM_CFS)) { + dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n"); + return -EINVAL; + } + + /* If the TDM interface is the clock master, it requires mclk */ + if (!iface->mclk && (fmt & SND_SOC_DAIFMT_CBS_CFS)) { + dev_err(dai->dev, "cpu clock master: mclk missing\n"); + return -ENODEV; + } + + iface->fmt = fmt; + return 0; +} + +static int axg_tdm_iface_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *ts = + snd_soc_dai_get_dma_data(dai, substream); + int ret; + + if (!axg_tdm_slots_total(ts->mask)) { + dev_err(dai->dev, "interface has not slots\n"); + return -EINVAL; + } + + /* Apply component wide rate symmetry */ + if (dai->component->active) { + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + iface->rate); + if (ret < 0) { + dev_err(dai->dev, + "can't set iface rate constraint\n"); + return ret; + } + } + + return 0; +} + +static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + unsigned int channels = params_channels(params); + unsigned int width = params_width(params); + + /* Save rate and sample_bits for component symmetry */ + iface->rate = params_rate(params); + + /* Make sure this interface can cope with the stream */ + if (axg_tdm_slots_total(ts->mask) < channels) { + dev_err(dai->dev, "not enough slots for channels\n"); + return -EINVAL; + } + + if (iface->slot_width < width) { + dev_err(dai->dev, "incompatible slots width for stream\n"); + return -EINVAL; + } + + /* Save the parameter for tdmout/tdmin widgets */ + ts->physical_width = params_physical_width(params); + ts->width = params_width(params); + ts->channels = params_channels(params); + + return 0; +} + +static int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + unsigned int ratio_num; + int ret; + + ret = clk_set_rate(iface->lrclk, params_rate(params)); + if (ret) { + dev_err(dai->dev, "setting sample clock failed: %d\n", ret); + return ret; + } + + switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + /* 50% duty cycle ratio */ + ratio_num = 1; + break; + + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* + * A zero duty cycle ratio will result in setting the mininum + * ratio possible which, for this clock, is 1 cycle of the + * parent bclk clock high and the rest low, This is exactly + * what we want here. + */ + ratio_num = 0; + break; + + default: + return -EINVAL; + } + + ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2); + if (ret) { + dev_err(dai->dev, + "setting sample clock duty cycle failed: %d\n", ret); + return ret; + } + + /* Set sample clock inversion */ + ret = clk_set_phase(iface->lrclk, + axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0); + if (ret) { + dev_err(dai->dev, + "setting sample clock phase failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + unsigned long srate; + int ret; + + srate = iface->slots * iface->slot_width * params_rate(params); + + if (!iface->mclk_rate) { + /* If no specific mclk is requested, default to bit clock * 4 */ + clk_set_rate(iface->mclk, 4 * srate); + } else { + /* Check if we can actually get the bit clock from mclk */ + if (iface->mclk_rate % srate) { + dev_err(dai->dev, + "can't derive sclk %lu from mclk %lu\n", + srate, iface->mclk_rate); + return -EINVAL; + } + } + + ret = clk_set_rate(iface->sclk, srate); + if (ret) { + dev_err(dai->dev, "setting bit clock failed: %d\n", ret); + return ret; + } + + /* Set the bit clock inversion */ + ret = clk_set_phase(iface->sclk, + axg_tdm_sclk_invert(iface->fmt) ? 0 : 180); + if (ret) { + dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret); + return ret; + } + + return ret; +} + +static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + if (iface->slots > 2) { + dev_err(dai->dev, "bad slot number for format: %d\n", + iface->slots); + return -EINVAL; + } + break; + + case SND_SOC_DAI_FORMAT_DSP_A: + case SND_SOC_DAI_FORMAT_DSP_B: + break; + + default: + dev_err(dai->dev, "unsupported dai format\n"); + return -EINVAL; + } + + ret = axg_tdm_iface_set_stream(substream, params, dai); + if (ret) + return ret; + + if (iface->fmt & SND_SOC_DAIFMT_CBS_CFS) { + ret = axg_tdm_iface_set_sclk(dai, params); + if (ret) + return ret; + + ret = axg_tdm_iface_set_lrclk(dai, params); + if (ret) + return ret; + } + + return 0; +} + +static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + + /* Stop all attached formatters */ + axg_tdm_stream_stop(ts); + + return 0; +} + +static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + + /* Force all attached formatters to update */ + return axg_tdm_stream_reset(ts); +} + +static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) +{ + if (dai->capture_dma_data) + axg_tdm_stream_free(dai->capture_dma_data); + + if (dai->playback_dma_data) + axg_tdm_stream_free(dai->playback_dma_data); + + return 0; +} + +static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) +{ + struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + + if (dai->capture_widget) { + dai->capture_dma_data = axg_tdm_stream_alloc(iface); + if (!dai->capture_dma_data) + return -ENOMEM; + } + + if (dai->playback_widget) { + dai->playback_dma_data = axg_tdm_stream_alloc(iface); + if (!dai->playback_dma_data) { + axg_tdm_iface_remove_dai(dai); + return -ENOMEM; + } + } + + return 0; +} + +static const struct snd_soc_dai_ops axg_tdm_iface_ops = { + .set_sysclk = axg_tdm_iface_set_sysclk, + .set_fmt = axg_tdm_iface_set_fmt, + .startup = axg_tdm_iface_startup, + .hw_params = axg_tdm_iface_hw_params, + .prepare = axg_tdm_iface_prepare, + .hw_free = axg_tdm_iface_hw_free, +}; + +/* TDM Backend DAIs */ +static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { + [TDM_IFACE_PAD] = { + .name = "TDM Pad", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = AXG_TDM_CHANNEL_MAX, + .rates = AXG_TDM_RATES, + .formats = AXG_TDM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AXG_TDM_CHANNEL_MAX, + .rates = AXG_TDM_RATES, + .formats = AXG_TDM_FORMATS, + }, + .id = TDM_IFACE_PAD, + .ops = &axg_tdm_iface_ops, + .probe = axg_tdm_iface_probe_dai, + .remove = axg_tdm_iface_remove_dai, + }, + [TDM_IFACE_LOOPBACK] = { + .name = "TDM Loopback", + .capture = { + .stream_name = "Loopback", + .channels_min = 1, + .channels_max = AXG_TDM_CHANNEL_MAX, + .rates = AXG_TDM_RATES, + .formats = AXG_TDM_FORMATS, + }, + .id = TDM_IFACE_LOOPBACK, + .ops = &axg_tdm_iface_ops, + .probe = axg_tdm_iface_probe_dai, + .remove = axg_tdm_iface_remove_dai, + }, +}; + +static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level now = + snd_soc_component_get_bias_level(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (now == SND_SOC_BIAS_STANDBY) + ret = clk_prepare_enable(iface->mclk); + break; + + case SND_SOC_BIAS_STANDBY: + if (now == SND_SOC_BIAS_PREPARE) + clk_disable_unprepare(iface->mclk); + break; + + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_ON: + break; + } + + return ret; +} + +static const struct snd_soc_component_driver axg_tdm_iface_component_drv = { + .set_bias_level = axg_tdm_iface_set_bias_level, +}; + +static const struct of_device_id axg_tdm_iface_of_match[] = { + { .compatible = "amlogic,axg-tdm-iface", }, + {} +}; +MODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match); + +static int axg_tdm_iface_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_dai_driver *dai_drv; + struct axg_tdm_iface *iface; + int ret, i; + + iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); + if (!iface) + return -ENOMEM; + platform_set_drvdata(pdev, iface); + + /* + * Duplicate dai driver: depending on the slot masks configuration + * We'll change the number of channel provided by DAI stream, so dpcm + * channel merge can be done properly + */ + dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), + sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) + memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], + sizeof(*dai_drv)); + + /* Bit clock provided on the pad */ + iface->sclk = devm_clk_get(dev, "sclk"); + if (IS_ERR(iface->sclk)) { + ret = PTR_ERR(iface->sclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get sclk: %d\n", ret); + return ret; + } + + /* Sample clock provided on the pad */ + iface->lrclk = devm_clk_get(dev, "lrclk"); + if (IS_ERR(iface->lrclk)) { + ret = PTR_ERR(iface->lrclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get lrclk: %d\n", ret); + return ret; + } + + /* + * mclk maybe be missing when the cpu dai is in slave mode and + * the codec does not require it to provide a master clock. + * At this point, ignore the error if mclk is missing. We'll + * throw an error if the cpu dai is master and mclk is missing + */ + iface->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(iface->mclk)) { + ret = PTR_ERR(iface->mclk); + if (ret == -ENOENT) { + iface->mclk = NULL; + } else { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get mclk: %d\n", ret); + return ret; + } + } + + return devm_snd_soc_register_component(dev, + &axg_tdm_iface_component_drv, dai_drv, + ARRAY_SIZE(axg_tdm_iface_dai_drv)); +} + +static struct platform_driver axg_tdm_iface_pdrv = { + .probe = axg_tdm_iface_probe, + .driver = { + .name = "axg-tdm-iface", + .of_match_table = axg_tdm_iface_of_match, + }, +}; +module_platform_driver(axg_tdm_iface_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM interface driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h new file mode 100644 index 000000000000..e578b6f40a07 --- /dev/null +++ b/sound/soc/meson/axg-tdm.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) + * + * Copyright (c) 2018 Baylibre SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef _MESON_AXG_TDM_H +#define _MESON_AXG_TDM_H + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#define AXG_TDM_NUM_LANES 4 +#define AXG_TDM_CHANNEL_MAX 128 +#define AXG_TDM_RATES (SNDRV_PCM_RATE_5512 | \ + SNDRV_PCM_RATE_8000_192000) +#define AXG_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +struct axg_tdm_iface { + struct clk *sclk; + struct clk *lrclk; + struct clk *mclk; + unsigned long mclk_rate; + + /* format is common to all the DAIs of the iface */ + unsigned int fmt; + unsigned int slots; + unsigned int slot_width; + + /* For component wide symmetry */ + int rate; +}; + +static inline bool axg_tdm_lrclk_invert(unsigned int fmt) +{ + return (fmt & SND_SOC_DAIFMT_I2S) ^ + !!(fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_NB_IF)); +} + +static inline bool axg_tdm_sclk_invert(unsigned int fmt) +{ + return fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_IB_NF); +} + +struct axg_tdm_stream { + struct axg_tdm_iface *iface; + struct list_head formatter_list; + struct mutex lock; + unsigned int channels; + unsigned int width; + unsigned int physical_width; + u32 *mask; + bool ready; +}; + +struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface); +void axg_tdm_stream_free(struct axg_tdm_stream *ts); +int axg_tdm_stream_start(struct axg_tdm_stream *ts); +void axg_tdm_stream_stop(struct axg_tdm_stream *ts); + +static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts) +{ + axg_tdm_stream_stop(ts); + return axg_tdm_stream_start(ts); +} + +int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, + u32 *rx_mask, unsigned int slots, + unsigned int slot_width); + +#endif /* _MESON_AXG_TDM_H */ diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c new file mode 100644 index 000000000000..bbac44c81688 --- /dev/null +++ b/sound/soc/meson/axg-tdmin.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm-formatter.h" + +#define TDMIN_CTRL 0x00 +#define TDMIN_CTRL_ENABLE BIT(31) +#define TDMIN_CTRL_I2S_MODE BIT(30) +#define TDMIN_CTRL_RST_OUT BIT(29) +#define TDMIN_CTRL_RST_IN BIT(28) +#define TDMIN_CTRL_WS_INV BIT(25) +#define TDMIN_CTRL_SEL_SHIFT 20 +#define TDMIN_CTRL_IN_BIT_SKEW_MASK GENMASK(18, 16) +#define TDMIN_CTRL_IN_BIT_SKEW(x) ((x) << 16) +#define TDMIN_CTRL_LSB_FIRST BIT(5) +#define TDMIN_CTRL_BITNUM_MASK GENMASK(4, 0) +#define TDMIN_CTRL_BITNUM(x) ((x) << 0) +#define TDMIN_SWAP 0x04 +#define TDMIN_MASK0 0x08 +#define TDMIN_MASK1 0x0c +#define TDMIN_MASK2 0x10 +#define TDMIN_MASK3 0x14 +#define TDMIN_STAT 0x18 +#define TDMIN_MUTE_VAL 0x1c +#define TDMIN_MUTE0 0x20 +#define TDMIN_MUTE1 0x24 +#define TDMIN_MUTE2 0x28 +#define TDMIN_MUTE3 0x2c + +static const struct regmap_config axg_tdmin_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TDMIN_MUTE3, +}; + +static const char * const axg_tdmin_sel_texts[] = { + "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 5", +}; + +/* Change to special mux control to reset dapm */ +static SOC_ENUM_SINGLE_DECL(axg_tdmin_sel_enum, TDMIN_CTRL, + TDMIN_CTRL_SEL_SHIFT, axg_tdmin_sel_texts); + +static const struct snd_kcontrol_new axg_tdmin_in_mux = + SOC_DAPM_ENUM("Input Source", axg_tdmin_sel_enum); + +static struct snd_soc_dai * +axg_tdmin_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->connect) + continue; + + if (p->source->id == snd_soc_dapm_dai_out) + return (struct snd_soc_dai *)p->source->priv; + + be = axg_tdmin_get_be(p->source); + if (be) + return be; + } + + return NULL; +} + +static struct axg_tdm_stream * +axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = axg_tdmin_get_be(w); + + if (!be) + return NULL; + + return be->capture_dma_data; +} + +static void axg_tdmin_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_RST_OUT | TDMIN_CTRL_RST_IN, 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_RST_OUT, TDMIN_CTRL_RST_OUT); + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_RST_IN, TDMIN_CTRL_RST_IN); + + /* Actually enable tdmin */ + regmap_update_bits(map, TDMIN_CTRL, + TDMIN_CTRL_ENABLE, TDMIN_CTRL_ENABLE); +} + +static void axg_tdmin_disable(struct regmap *map) +{ + regmap_update_bits(map, TDMIN_CTRL, TDMIN_CTRL_ENABLE, 0); +} + +static int axg_tdmin_prepare(struct regmap *map, struct axg_tdm_stream *ts) +{ + unsigned int val = 0; + + /* Set stream skew */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + val |= TDMIN_CTRL_IN_BIT_SKEW(3); + break; + + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_B: + val = TDMIN_CTRL_IN_BIT_SKEW(2); + break; + + default: + pr_err("Unsupported format: %u\n", + ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* Set stream format mode */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + val |= TDMIN_CTRL_I2S_MODE; + break; + } + + /* If the sample clock is inverted, invert it back for the formatter */ + if (axg_tdm_lrclk_invert(ts->iface->fmt)) + val |= TDMIN_CTRL_WS_INV; + + /* Set the slot width */ + val |= TDMIN_CTRL_BITNUM(ts->iface->slot_width - 1); + + /* + * The following also reset LSB_FIRST which result in the formatter + * placing the first bit received at bit 31 + */ + regmap_update_bits(map, TDMIN_CTRL, + (TDMIN_CTRL_IN_BIT_SKEW_MASK | TDMIN_CTRL_WS_INV | + TDMIN_CTRL_I2S_MODE | TDMIN_CTRL_LSB_FIRST | + TDMIN_CTRL_BITNUM_MASK), val); + + /* Set static swap mask configuration */ + regmap_write(map, TDMIN_SWAP, 0x76543210); + + return axg_tdm_formatter_set_channel_masks(map, ts, TDMIN_MASK0); +} + +static const struct snd_soc_dapm_widget axg_tdmin_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 5", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmin_in_mux), + SND_SOC_DAPM_PGA_E("DEC", SND_SOC_NOPM, 0, 0, NULL, 0, + axg_tdm_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_tdmin_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "SRC SEL", "IN 3", "IN 3" }, + { "SRC SEL", "IN 4", "IN 4" }, + { "SRC SEL", "IN 5", "IN 5" }, + { "DEC", NULL, "SRC SEL" }, + { "OUT", NULL, "DEC" }, +}; + +static const struct snd_soc_component_driver axg_tdmin_component_drv = { + .dapm_widgets = axg_tdmin_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_tdmin_dapm_widgets), + .dapm_routes = axg_tdmin_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_tdmin_dapm_routes), +}; + +static const struct axg_tdm_formatter_ops axg_tdmin_ops = { + .get_stream = axg_tdmin_get_tdm_stream, + .prepare = axg_tdmin_prepare, + .enable = axg_tdmin_enable, + .disable = axg_tdmin_disable, +}; + +static const struct axg_tdm_formatter_driver axg_tdmin_drv = { + .component_drv = &axg_tdmin_component_drv, + .regmap_cfg = &axg_tdmin_regmap_cfg, + .ops = &axg_tdmin_ops, + .invert_sclk = false, +}; + +static const struct of_device_id axg_tdmin_of_match[] = { + { + .compatible = "amlogic,axg-tdmin", + .data = &axg_tdmin_drv, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_tdmin_of_match); + +static struct platform_driver axg_tdmin_pdrv = { + .probe = axg_tdm_formatter_probe, + .driver = { + .name = "axg-tdmin", + .of_match_table = axg_tdmin_of_match, + }, +}; +module_platform_driver(axg_tdmin_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM input formatter driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c new file mode 100644 index 000000000000..f73368ee1088 --- /dev/null +++ b/sound/soc/meson/axg-tdmout.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-tdm-formatter.h" + +#define TDMOUT_CTRL0 0x00 +#define TDMOUT_CTRL0_BITNUM_MASK GENMASK(4, 0) +#define TDMOUT_CTRL0_BITNUM(x) ((x) << 0) +#define TDMOUT_CTRL0_SLOTNUM_MASK GENMASK(9, 5) +#define TDMOUT_CTRL0_SLOTNUM(x) ((x) << 5) +#define TDMOUT_CTRL0_INIT_BITNUM_MASK GENMASK(19, 15) +#define TDMOUT_CTRL0_INIT_BITNUM(x) ((x) << 15) +#define TDMOUT_CTRL0_ENABLE BIT(31) +#define TDMOUT_CTRL0_RST_OUT BIT(29) +#define TDMOUT_CTRL0_RST_IN BIT(28) +#define TDMOUT_CTRL1 0x04 +#define TDMOUT_CTRL1_TYPE_MASK GENMASK(6, 4) +#define TDMOUT_CTRL1_TYPE(x) ((x) << 4) +#define TDMOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8) +#define TDMOUT_CTRL1_MSB_POS(x) ((x) << 8) +#define TDMOUT_CTRL1_SEL_SHIFT 24 +#define TDMOUT_CTRL1_GAIN_EN 26 +#define TDMOUT_CTRL1_WS_INV BIT(28) +#define TDMOUT_SWAP 0x08 +#define TDMOUT_MASK0 0x0c +#define TDMOUT_MASK1 0x10 +#define TDMOUT_MASK2 0x14 +#define TDMOUT_MASK3 0x18 +#define TDMOUT_STAT 0x1c +#define TDMOUT_GAIN0 0x20 +#define TDMOUT_GAIN1 0x24 +#define TDMOUT_MUTE_VAL 0x28 +#define TDMOUT_MUTE0 0x2c +#define TDMOUT_MUTE1 0x30 +#define TDMOUT_MUTE2 0x34 +#define TDMOUT_MUTE3 0x38 +#define TDMOUT_MASK_VAL 0x3c + +static const struct regmap_config axg_tdmout_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TDMOUT_MASK_VAL, +}; + +static const struct snd_kcontrol_new axg_tdmout_controls[] = { + SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0), + SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0), + SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0), + SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0), + SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1, + TDMOUT_CTRL1_GAIN_EN, 1, 0), +}; + +static const char * const tdmout_sel_texts[] = { + "IN 0", "IN 1", "IN 2", +}; + +static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1, + TDMOUT_CTRL1_SEL_SHIFT, tdmout_sel_texts); + +static const struct snd_kcontrol_new axg_tdmout_in_mux = + SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum); + +static struct snd_soc_dai * +axg_tdmout_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + if (p->sink->id == snd_soc_dapm_dai_in) + return (struct snd_soc_dai *)p->sink->priv; + + be = axg_tdmout_get_be(p->sink); + if (be) + return be; + } + + return NULL; +} + +static struct axg_tdm_stream * +axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = axg_tdmout_get_be(w); + + if (!be) + return NULL; + + return be->playback_dma_data; +} + +static void axg_tdmout_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_OUT | TDMOUT_CTRL0_RST_IN, 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_OUT, TDMOUT_CTRL0_RST_OUT); + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_IN, TDMOUT_CTRL0_RST_IN); + + /* Actually enable tdmout */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_ENABLE, TDMOUT_CTRL0_ENABLE); +} + +static void axg_tdmout_disable(struct regmap *map) +{ + regmap_update_bits(map, TDMOUT_CTRL0, TDMOUT_CTRL0_ENABLE, 0); +} + +static int axg_tdmout_prepare(struct regmap *map, struct axg_tdm_stream *ts) +{ + unsigned int val = 0; + + /* Set the stream skew */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + val |= TDMOUT_CTRL0_INIT_BITNUM(1); + break; + + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_B: + val |= TDMOUT_CTRL0_INIT_BITNUM(2); + break; + + default: + pr_err("Unsupported format: %u\n", + ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* Set the slot width */ + val |= TDMOUT_CTRL0_BITNUM(ts->iface->slot_width - 1); + + /* Set the slot number */ + val |= TDMOUT_CTRL0_SLOTNUM(ts->iface->slots - 1); + + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_INIT_BITNUM_MASK | + TDMOUT_CTRL0_BITNUM_MASK | + TDMOUT_CTRL0_SLOTNUM_MASK, val); + + /* Set the sample width */ + val = TDMOUT_CTRL1_MSB_POS(ts->width - 1); + + /* FIFO data are arranged in chunks of 64bits */ + switch (ts->physical_width) { + case 8: + /* 8 samples of 8 bits */ + val |= TDMOUT_CTRL1_TYPE(0); + break; + case 16: + /* 4 samples of 16 bits - right justified */ + val |= TDMOUT_CTRL1_TYPE(2); + break; + case 32: + /* 2 samples of 32 bits - right justified */ + val |= TDMOUT_CTRL1_TYPE(4); + break; + default: + pr_err("Unsupported physical width: %u\n", + ts->physical_width); + return -EINVAL; + } + + /* If the sample clock is inverted, invert it back for the formatter */ + if (axg_tdm_lrclk_invert(ts->iface->fmt)) + val |= TDMOUT_CTRL1_WS_INV; + + regmap_update_bits(map, TDMOUT_CTRL1, + (TDMOUT_CTRL1_TYPE_MASK | TDMOUT_CTRL1_MSB_POS_MASK | + TDMOUT_CTRL1_WS_INV), val); + + /* Set static swap mask configuration */ + regmap_write(map, TDMOUT_SWAP, 0x76543210); + + return axg_tdm_formatter_set_channel_masks(map, ts, TDMOUT_MASK0); +} + +static const struct snd_soc_dapm_widget axg_tdmout_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmout_in_mux), + SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0, + axg_tdm_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_tdmout_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "ENC", NULL, "SRC SEL" }, + { "OUT", NULL, "ENC" }, +}; + +static const struct snd_soc_component_driver axg_tdmout_component_drv = { + .controls = axg_tdmout_controls, + .num_controls = ARRAY_SIZE(axg_tdmout_controls), + .dapm_widgets = axg_tdmout_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_tdmout_dapm_widgets), + .dapm_routes = axg_tdmout_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_tdmout_dapm_routes), +}; + +static const struct axg_tdm_formatter_ops axg_tdmout_ops = { + .get_stream = axg_tdmout_get_tdm_stream, + .prepare = axg_tdmout_prepare, + .enable = axg_tdmout_enable, + .disable = axg_tdmout_disable, +}; + +static const struct axg_tdm_formatter_driver axg_tdmout_drv = { + .component_drv = &axg_tdmout_component_drv, + .regmap_cfg = &axg_tdmout_regmap_cfg, + .ops = &axg_tdmout_ops, + .invert_sclk = true, +}; + +static const struct of_device_id axg_tdmout_of_match[] = { + { + .compatible = "amlogic,axg-tdmout", + .data = &axg_tdmout_drv, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_tdmout_of_match); + +static struct platform_driver axg_tdmout_pdrv = { + .probe = axg_tdm_formatter_probe, + .driver = { + .name = "axg-tdmout", + .of_match_table = axg_tdmout_of_match, + }, +}; +module_platform_driver(axg_tdmout_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM output formatter driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c new file mode 100644 index 000000000000..c2c9bb312586 --- /dev/null +++ b/sound/soc/meson/axg-toddr.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +/* This driver implements the frontend capture DAI of AXG based SoCs */ + +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "axg-fifo.h" + +#define CTRL0_TODDR_SEL_RESAMPLE BIT(30) +#define CTRL0_TODDR_EXT_SIGNED BIT(29) +#define CTRL0_TODDR_PP_MODE BIT(28) +#define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) +#define CTRL0_TODDR_TYPE(x) ((x) << 13) +#define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) +#define CTRL0_TODDR_MSB_POS(x) ((x) << 8) +#define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) +#define CTRL0_TODDR_LSB_POS(x) ((x) << 3) + +static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE); +} + +static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + unsigned int type, width, msb = 31; + + /* + * NOTE: + * Almost all backend will place the MSB at bit 31, except SPDIF Input + * which will put it at index 28. When adding support for the SPDIF + * Input, we'll need to find which type of backend we are connected to. + */ + + switch (params_physical_width(params)) { + case 8: + type = 0; /* 8 samples of 8 bits */ + break; + case 16: + type = 2; /* 4 samples of 16 bits - right justified */ + break; + case 32: + type = 4; /* 2 samples of 32 bits - right justified */ + break; + default: + return -EINVAL; + } + + width = params_width(params); + + regmap_update_bits(fifo->map, FIFO_CTRL0, + CTRL0_TODDR_TYPE_MASK | + CTRL0_TODDR_MSB_POS_MASK | + CTRL0_TODDR_LSB_POS_MASK, + CTRL0_TODDR_TYPE(type) | + CTRL0_TODDR_MSB_POS(msb) | + CTRL0_TODDR_LSB_POS(msb - (width - 1))); + + return 0; +} + +static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + unsigned int fifo_threshold; + int ret; + + /* Enable pclk to access registers and clock the fifo ip */ + ret = clk_prepare_enable(fifo->pclk); + if (ret) + return ret; + + /* Select orginal data - resampling not supported ATM */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0); + + /* Only signed format are supported ATM */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED, + CTRL0_TODDR_EXT_SIGNED); + + /* Apply single buffer mode to the interface */ + regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0); + + /* TODDR does not have a configurable fifo depth */ + fifo_threshold = AXG_FIFO_MIN_CNT - 1; + regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK, + CTRL1_THRESHOLD(fifo_threshold)); + + return 0; +} + +static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(fifo->pclk); +} + +static const struct snd_soc_dai_ops axg_toddr_ops = { + .hw_params = axg_toddr_dai_hw_params, + .startup = axg_toddr_dai_startup, + .shutdown = axg_toddr_dai_shutdown, +}; + +static struct snd_soc_dai_driver axg_toddr_dai_drv = { + .name = "TODDR", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = AXG_FIFO_CH_MAX, + .rates = AXG_FIFO_RATES, + .formats = AXG_FIFO_FORMATS, + }, + .ops = &axg_toddr_ops, + .pcm_new = axg_toddr_pcm_new, +}; + +static const char * const axg_toddr_sel_texts[] = { + "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6" +}; + +static const unsigned int axg_toddr_sel_values[] = { + 0, 1, 2, 3, 4, 6 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, + CTRL0_SEL_SHIFT, CTRL0_SEL_MASK, + axg_toddr_sel_texts, axg_toddr_sel_values); + +static const struct snd_kcontrol_new axg_toddr_in_mux = + SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum); + +static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = { + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux), + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = { + { "Capture", NULL, "SRC SEL" }, + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "SRC SEL", "IN 3", "IN 3" }, + { "SRC SEL", "IN 4", "IN 4" }, + { "SRC SEL", "IN 6", "IN 6" }, +}; + +static const struct snd_soc_component_driver axg_toddr_component_drv = { + .dapm_widgets = axg_toddr_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), + .dapm_routes = axg_toddr_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), + .ops = &axg_fifo_pcm_ops +}; + +static const struct axg_fifo_match_data axg_toddr_match_data = { + .component_drv = &axg_toddr_component_drv, + .dai_drv = &axg_toddr_dai_drv +}; + +static const struct of_device_id axg_toddr_of_match[] = { + { + .compatible = "amlogic,axg-toddr", + .data = &axg_toddr_match_data, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_toddr_of_match); + +static struct platform_driver axg_toddr_pdrv = { + .probe = axg_fifo_probe, + .driver = { + .name = "axg-toddr", + .of_match_table = axg_toddr_of_match, + }, +}; +module_platform_driver(axg_toddr_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG capture fifo driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 15ccbf479c96..d5ae9eb8c756 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -40,7 +40,7 @@ struct abe_twl6040 { int mclk_freq; /* MCLK frequency speed for twl6040 */ }; -struct platform_device *dmic_codec_dev; +static struct platform_device *dmic_codec_dev; static int omap_abe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 51dd7c65096b..fe966272bd0c 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -213,8 +213,10 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, switch (channels) { case 6: dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE; + /* fall through */ case 4: dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE; + /* fall through */ case 2: dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE; break; diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 0e97360f9890..4c1be36c2207 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -310,15 +310,19 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, /* up to 3 channels for capture */ return -EINVAL; link_mask |= 1 << 4; + /* fall through */ case 4: if (stream == SNDRV_PCM_STREAM_CAPTURE) /* up to 3 channels for capture */ return -EINVAL; link_mask |= 1 << 3; + /* fall through */ case 3: link_mask |= 1 << 2; + /* fall through */ case 2: link_mask |= 1 << 1; + /* fall through */ case 1: link_mask |= 1 << 0; break; diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 960744e46edc..776e148b0aa2 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -24,15 +24,19 @@ config SND_PXA2XX_AC97 config SND_PXA2XX_SOC_AC97 tristate select AC97_BUS + select SND_PXA2XX_LIB select SND_PXA2XX_LIB_AC97 select SND_SOC_AC97_BUS config SND_PXA2XX_SOC_I2S + select SND_PXA2XX_LIB tristate config SND_PXA_SOC_SSP - tristate + tristate "Soc Audio via PXA2xx/PXA3xx SSP ports" + depends on PLAT_PXA select PXA_SSP + select SND_PXA2XX_LIB config SND_MMP_SOC_SSPA tristate diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 2fc012b06c43..935a248e5bf6 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -90,95 +90,9 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int acps, acds, width; - unsigned int div4 = PXA_SSP_CLK_SCDB_4; + unsigned int width; int ret = 0; - width = snd_pcm_format_physical_width(params_format(params)); - - /* - * rate = SSPSCLK / (2 * width(16 or 32)) - * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1) - */ - switch (params_rate(params)) { - case 8000: - /* off by a factor of 2: bug in the PXA27x audio clock? */ - acps = 32842000; - switch (width) { - case 16: - /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_16; - break; - default: /* 32 */ - /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_8; - } - break; - case 11025: - acps = 5622000; - switch (width) { - case 16: - /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_4; - break; - default: /* 32 */ - /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - } - break; - case 22050: - acps = 5622000; - switch (width) { - case 16: - /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - break; - default: /* 32 */ - /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - } - break; - case 44100: - acps = 5622000; - switch (width) { - case 16: - /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - break; - default: /* 32 */ - /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - } - break; - case 48000: - acps = 12235000; - switch (width) { - case 16: - /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - break; - default: /* 32 */ - /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - } - break; - case 96000: - default: - acps = 12235000; - switch (width) { - case 16: - /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; - break; - default: /* 32 */ - /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - div4 = PXA_SSP_CLK_SCDB_1; - break; - } - break; - } - /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); @@ -191,6 +105,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; + width = snd_pcm_format_physical_width(params_format(params)); ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width); if (ret < 0) return ret; @@ -201,23 +116,6 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* set the SSP audio system clock ACDS divider */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - PXA_SSP_AUDIO_DIV_ACDS, acds); - if (ret < 0) - return ret; - - /* set the SSP audio system clock SCDB divider4 */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - PXA_SSP_AUDIO_DIV_SCDB, div4); - if (ret < 0) - return ret; - - /* set SSP audio pll clock */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps); - if (ret < 0) - return ret; - return 0; } diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 6fc986080130..69033e1a84e6 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -34,7 +34,6 @@ #include <sound/pxa2xx-lib.h> #include <sound/dmaengine_pcm.h> -#include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" /* @@ -42,6 +41,8 @@ */ struct ssp_priv { struct ssp_device *ssp; + struct clk *extclk; + unsigned long ssp_clk; unsigned int sysclk; unsigned int dai_fmt; unsigned int configured_dai_fmt; @@ -105,9 +106,8 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - - dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - &ssp->drcmr_tx : &ssp->drcmr_rx; + dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "tx" : "rx"; snd_soc_dai_set_dma_data(cpu_dai, substream, dma); @@ -194,21 +194,6 @@ static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) pxa_ssp_write_reg(ssp, SSCR0, sscr0); } -/** - * pxa_ssp_get_clkdiv - get SSP clock divider - */ -static u32 pxa_ssp_get_scr(struct ssp_device *ssp) -{ - u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); - u32 div; - - if (ssp->type == PXA25x_SSP) - div = ((sscr0 >> 8) & 0xff) * 2 + 2; - else - div = ((sscr0 >> 8) & 0xfff) + 1; - return div; -} - /* * Set the SSP ports SYSCLK. */ @@ -221,6 +206,21 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + if (priv->extclk) { + int ret; + + /* + * For DT based boards, if an extclk is given, use it + * here and configure PXA_SSP_CLK_EXT. + */ + + ret = clk_set_rate(priv->extclk, freq); + if (ret < 0) + return ret; + + clk_id = PXA_SSP_CLK_EXT; + } + dev_dbg(&ssp->pdev->dev, "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", cpu_dai->id, clk_id, freq); @@ -265,66 +265,17 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, } /* - * Set the SSP clock dividers. - */ -static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); - struct ssp_device *ssp = priv->ssp; - int val; - - switch (div_id) { - case PXA_SSP_AUDIO_DIV_ACDS: - val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_AUDIO_DIV_SCDB: - val = pxa_ssp_read_reg(ssp, SSACD); - val &= ~SSACD_SCDB; - if (ssp->type == PXA3xx_SSP) - val &= ~SSACD_SCDX8; - switch (div) { - case PXA_SSP_CLK_SCDB_1: - val |= SSACD_SCDB; - break; - case PXA_SSP_CLK_SCDB_4: - break; - case PXA_SSP_CLK_SCDB_8: - if (ssp->type == PXA3xx_SSP) - val |= SSACD_SCDX8; - else - return -EINVAL; - break; - default: - return -EINVAL; - } - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_DIV_SCR: - pxa_ssp_set_scr(ssp, div); - break; - default: - return -ENODEV; - } - - return 0; -} - -/* * Configure the PLL frequency pxa27x and (afaik - pxa320 only) */ -static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, - int source, unsigned int freq_in, unsigned int freq_out) +static int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; if (ssp->type == PXA3xx_SSP) pxa_ssp_write_reg(ssp, SSACDD, 0); - switch (freq_out) { + switch (freq) { case 5622000: break; case 11345000: @@ -355,7 +306,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, u64 tmp = 19968; tmp *= 1000000; - do_div(tmp, freq_out); + do_div(tmp, freq); val = tmp; val = (val << 16) | 64; @@ -365,7 +316,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, dev_dbg(&ssp->pdev->dev, "Using SSACDD %x to supply %uHz\n", - val, freq_out); + val, freq); break; } @@ -535,6 +486,7 @@ static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) case SND_SOC_DAIFMT_DSP_A: sspsp |= SSPSP_FSRT; + /* fall through */ case SND_SOC_DAIFMT_DSP_B: sscr0 |= SSCR0_MOD | SSCR0_PSP; sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; @@ -570,6 +522,24 @@ static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) return 0; } +struct pxa_ssp_clock_mode { + int rate; + int pll; + u8 acds; + u8 scdb; +}; + +static const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = { + { .rate = 8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X }, + { .rate = 11025, .pll = 5622000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_4X }, + { .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X }, + { .rate = 22050, .pll = 5622000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, + { .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_1X }, + {} +}; + /* * Set the SSP audio DMA parameters and sample size. * Can be called multiple times by oss emulation. @@ -581,11 +551,12 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; int chn = params_channels(params); - u32 sscr0; - u32 sspsp; + u32 sscr0, sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; struct snd_dmaengine_dai_dma_data *dma_data; + int rate = params_rate(params); + int bclk = rate * chn * (width / 8); int ret; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -625,11 +596,57 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, } pxa_ssp_write_reg(ssp, SSCR0, sscr0); + if (sscr0 & SSCR0_ACS) { + ret = pxa_ssp_set_pll(priv, bclk); + + /* + * If we were able to generate the bclk directly, + * all is fine. Otherwise, look up the closest rate + * from the table and also set the dividers. + */ + + if (ret < 0) { + const struct pxa_ssp_clock_mode *m; + int ssacd, acds; + + for (m = pxa_ssp_clock_modes; m->rate; m++) { + if (m->rate == rate) + break; + } + + if (!m->rate) + return -EINVAL; + + acds = m->acds; + + /* The values in the table are for 16 bits */ + if (width == 32) + acds--; + + ret = pxa_ssp_set_pll(priv, bclk); + if (ret < 0) + return ret; + + ssacd = pxa_ssp_read_reg(ssp, SSACD); + ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X); + ssacd |= SSACD_ACDS(m->acds); + ssacd |= m->scdb; + pxa_ssp_write_reg(ssp, SSACD, ssacd); + } + } else if (sscr0 & SSCR0_ECS) { + /* + * For setups with external clocking, the PLL and its diviers + * are not active. Instead, the SCR bits in SSCR0 can be used + * to divide the clock. + */ + pxa_ssp_set_scr(ssp, bclk / rate); + } + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: sspsp = pxa_ssp_read_reg(ssp, SSPSP); - if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) { + if (((priv->sysclk / bclk) == 64) && (width == 16)) { /* This is a special case where the bitclk is 64fs * and we're not dealing with 2*32 bits of audio * samples. @@ -773,6 +790,15 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) ret = -ENODEV; goto err_priv; } + + priv->extclk = devm_clk_get(dev, "extclk"); + if (IS_ERR(priv->extclk)) { + ret = PTR_ERR(priv->extclk); + if (ret == -EPROBE_DEFER) + return ret; + + priv->extclk = NULL; + } } else { priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); if (priv->ssp == NULL) { @@ -814,8 +840,6 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { .trigger = pxa_ssp_trigger, .hw_params = pxa_ssp_hw_params, .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, .set_fmt = pxa_ssp_set_dai_fmt, .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, .set_tristate = pxa_ssp_set_dai_tristate, @@ -843,6 +867,9 @@ static struct snd_soc_dai_driver pxa_ssp_dai = { static const struct snd_soc_component_driver pxa_ssp_component = { .name = "pxa-ssp", + .ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_soc_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, }; #ifdef CONFIG_OF diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 803818aabee9..9f779657bc86 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -68,61 +68,39 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 11, -}; - static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "pcm_pcm_stereo_in", .maxburst = 32, - .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, -}; - -static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 12, }; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { .addr = __PREG(PCDR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "pcm_pcm_stereo_out", .maxburst = 32, - .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 10, -}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .chan_name = "pcm_aux_mono_out", .maxburst = 16, - .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 9, -}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { .addr = __PREG(MODR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .chan_name = "pcm_aux_mono_in", .maxburst = 16, - .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = { - .prio = PXAD_PRIO_LOWEST, - .drcmr = 8, -}; static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { .addr = __PREG(MCDR), .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .chan_name = "pcm_aux_mic_mono", .maxburst = 16, - .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, @@ -236,7 +214,21 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { static const struct snd_soc_component_driver pxa_ac97_component = { .name = "pxa-ac97", + .ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_soc_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pxa2xx_ac97_dt_ids[] = { + { .compatible = "marvell,pxa250-ac97", }, + { .compatible = "marvell,pxa270-ac97", }, + { .compatible = "marvell,pxa300-ac97", }, + { } }; +MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); + +#endif static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) { @@ -296,6 +288,7 @@ static struct platform_driver pxa2xx_ac97_driver = { #ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, #endif + .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), }, }; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 3fb60baf6eab..42820121e5b9 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -82,20 +82,18 @@ static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; static int clk_ena = 0; -static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { .addr = __PREG(SADR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "tx", .maxburst = 32, - .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { .addr = __PREG(SADR), .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .chan_name = "rx", .maxburst = 32, - .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, @@ -366,6 +364,9 @@ static struct snd_soc_dai_driver pxa_i2s_dai = { static const struct snd_soc_component_driver pxa_i2s_component = { .name = "pxa-i2s", + .ops = &pxa2xx_pcm_ops, + .pcm_new = pxa2xx_soc_pcm_new, + .pcm_free = pxa2xx_pcm_free_dma_buffers, }; static int pxa2xx_i2s_drv_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 8b6a70e94c01..72eaaef1b426 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -20,70 +20,6 @@ #include <sound/pxa2xx-lib.h> #include <sound/dmaengine_pcm.h> -#include "../../arm/pxa2xx-pcm.h" - -static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_dmaengine_dai_dma_data *dma; - - dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!dma) - return 0; - - return __pxa2xx_pcm_hw_params(substream, params); -} - -static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - __pxa2xx_pcm_hw_free(substream); - - return 0; -} - -static const struct snd_pcm_ops pxa2xx_pcm_ops = { - .open = __pxa2xx_pcm_open, - .close = __pxa2xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pxa2xx_pcm_hw_params, - .hw_free = pxa2xx_pcm_hw_free, - .prepare = __pxa2xx_pcm_prepare, - .trigger = pxa2xx_pcm_trigger, - .pointer = pxa2xx_pcm_pointer, - .mmap = pxa2xx_pcm_mmap, -}; - -static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; -} - static const struct snd_soc_component_driver pxa2xx_soc_platform = { .ops = &pxa2xx_pcm_ops, .pcm_new = pxa2xx_soc_pcm_new, @@ -96,18 +32,9 @@ static int pxa2xx_soc_platform_probe(struct platform_device *pdev) NULL, 0); } -#ifdef CONFIG_OF -static const struct of_device_id snd_soc_pxa_audio_match[] = { - { .compatible = "mrvl,pxa-pcm-audio" }, - { } -}; -MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match); -#endif - static struct platform_driver pxa_pcm_driver = { .driver = { .name = "pxa-pcm-audio", - .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), }, .probe = pxa2xx_soc_platform_probe, diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index ba468e560dd2..230eee450f45 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -83,11 +83,9 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int pll_out = 0; unsigned int wm9713_div = 0; int ret = 0; int rate = params_rate(params); - int width = snd_pcm_format_physical_width(params_format(params)); /* Only support ratios that we can generate neatly from the AC97 * based master clock - in particular, this excludes 44.1kHz. @@ -109,17 +107,10 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* Add 1 to the width for the leading clock cycle */ - pll_out = rate * (width + 1) * 8; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out); - if (ret < 0) - return ret; - if (clk_pout) ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV, WM9713_PCMDIV(wm9713_div)); diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 87838fa27997..2a4c912d1e48 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -41,6 +41,9 @@ config SND_SOC_APQ8016_SBC APQ8016 SOC-based systems. Say Y if you want to use audio devices on MI2S. +config SND_SOC_QCOM_COMMON + tristate + config SND_SOC_QDSP6_COMMON tristate @@ -86,7 +89,18 @@ config SND_SOC_MSM8996 tristate "SoC Machine driver for MSM8996 and APQ8096 boards" depends on QCOM_APR select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON help Support for Qualcomm Technologies LPASS audio block in APQ8096 SoC-based systems. Say Y if you want to use audio device on this SoCs + +config SND_SOC_SDM845 + tristate "SoC Machine driver for SDM845 boards" + depends on QCOM_APR + select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON + help + To add support for audio on Qualcomm Technologies Inc. + SDM845 SoC-based systems. + Say Y if you want to use audio device on this SoCs. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 206945bb9ba1..41b2c7a23a4d 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -14,10 +14,14 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o +snd-soc-sdm845-objs := sdm845.o +snd-soc-qcom-common-objs := common.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o +obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o #DSP lib obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 561cd429e6f2..1543e85629f8 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018, Linaro Limited -#include <linux/soc/qcom/apr.h> #include <linux/module.h> -#include <linux/component.h> #include <linux/platform_device.h> #include <linux/of_device.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/pcm.h> +#include "common.h" static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) @@ -24,211 +23,57 @@ static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int apq8096_sbc_parse_of(struct snd_soc_card *card) +static void apq8096_add_be_ops(struct snd_soc_card *card) { - struct device_node *np; - struct device_node *codec = NULL; - struct device_node *platform = NULL; - struct device_node *cpu = NULL; - struct device *dev = card->dev; - struct snd_soc_dai_link *link; - int ret, num_links; - - ret = snd_soc_of_parse_card_name(card, "qcom,model"); - if (ret) { - dev_err(dev, "Error parsing card name: %d\n", ret); - return ret; - } - - /* DAPM routes */ - if (of_property_read_bool(dev->of_node, "qcom,audio-routing")) { - ret = snd_soc_of_parse_audio_routing(card, - "qcom,audio-routing"); - if (ret) - return ret; - } - - /* Populate links */ - num_links = of_get_child_count(dev->of_node); + struct snd_soc_dai_link *link = card->dai_link; + int i, num_links = card->num_links; - /* Allocate the DAI link array */ - card->dai_link = kcalloc(num_links, sizeof(*link), GFP_KERNEL); - if (!card->dai_link) - return -ENOMEM; - - card->num_links = num_links; - link = card->dai_link; - - for_each_child_of_node(dev->of_node, np) { - cpu = of_get_child_by_name(np, "cpu"); - if (!cpu) { - dev_err(dev, "Can't find cpu DT node\n"); - ret = -EINVAL; - goto err; - } - - link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); - if (!link->cpu_of_node) { - dev_err(card->dev, "error getting cpu phandle\n"); - ret = -EINVAL; - goto err; - } - - ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); - if (ret) { - dev_err(card->dev, "error getting cpu dai name\n"); - goto err; - } - - platform = of_get_child_by_name(np, "platform"); - codec = of_get_child_by_name(np, "codec"); - if (codec && platform) { - link->platform_of_node = of_parse_phandle(platform, - "sound-dai", - 0); - if (!link->platform_of_node) { - dev_err(card->dev, "platform dai not found\n"); - ret = -EINVAL; - goto err; - } - - ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); - if (ret < 0) { - dev_err(card->dev, "codec dai not found\n"); - goto err; - } - link->no_pcm = 1; - link->ignore_pmdown_time = 1; + for (i = 0; i < num_links; i++) { + if (link->no_pcm == 1) link->be_hw_params_fixup = apq8096_be_hw_params_fixup; - } else { - link->platform_of_node = link->cpu_of_node; - link->codec_dai_name = "snd-soc-dummy-dai"; - link->codec_name = "snd-soc-dummy"; - link->dynamic = 1; - } - - link->ignore_suspend = 1; - ret = of_property_read_string(np, "link-name", &link->name); - if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); - goto err; - } - - link->dpcm_playback = 1; - link->dpcm_capture = 1; - link->stream_name = link->name; link++; } - - return 0; -err: - of_node_put(cpu); - of_node_put(codec); - of_node_put(platform); - kfree(card->dai_link); - return ret; } -static int apq8096_bind(struct device *dev) +static int apq8096_platform_probe(struct platform_device *pdev) { struct snd_soc_card *card; + struct device *dev = &pdev->dev; int ret; card = kzalloc(sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - component_bind_all(dev, card); card->dev = dev; - ret = apq8096_sbc_parse_of(card); + dev_set_drvdata(dev, card); + ret = qcom_snd_parse_of(card); if (ret) { dev_err(dev, "Error parsing OF data\n"); goto err; } + apq8096_add_be_ops(card); ret = snd_soc_register_card(card); if (ret) - goto err; + goto err_card_register; return 0; +err_card_register: + kfree(card->dai_link); err: - component_unbind_all(dev, card); kfree(card); return ret; } -static void apq8096_unbind(struct device *dev) +static int apq8096_platform_remove(struct platform_device *pdev) { - struct snd_soc_card *card = dev_get_drvdata(dev); + struct snd_soc_card *card = dev_get_drvdata(&pdev->dev); snd_soc_unregister_card(card); - component_unbind_all(dev, card); kfree(card->dai_link); kfree(card); -} - -static const struct component_master_ops apq8096_ops = { - .bind = apq8096_bind, - .unbind = apq8096_unbind, -}; - -static int apq8016_compare_of(struct device *dev, void *data) -{ - return dev->of_node == data; -} - -static void apq8016_release_of(struct device *dev, void *data) -{ - of_node_put(data); -} - -static int add_audio_components(struct device *dev, - struct component_match **matchptr) -{ - struct device_node *np, *platform, *cpu, *node, *dai_node; - - node = dev->of_node; - - for_each_child_of_node(node, np) { - cpu = of_get_child_by_name(np, "cpu"); - if (cpu) { - dai_node = of_parse_phandle(cpu, "sound-dai", 0); - of_node_get(dai_node); - component_match_add_release(dev, matchptr, - apq8016_release_of, - apq8016_compare_of, - dai_node); - } - - platform = of_get_child_by_name(np, "platform"); - if (platform) { - dai_node = of_parse_phandle(platform, "sound-dai", 0); - component_match_add_release(dev, matchptr, - apq8016_release_of, - apq8016_compare_of, - dai_node); - } - } - - return 0; -} - -static int apq8096_platform_probe(struct platform_device *pdev) -{ - struct component_match *match = NULL; - int ret; - - ret = add_audio_components(&pdev->dev, &match); - if (ret) - return ret; - - return component_master_add_with_match(&pdev->dev, &apq8096_ops, match); -} - -static int apq8096_platform_remove(struct platform_device *pdev) -{ - component_master_del(&pdev->dev, &apq8096_ops); return 0; } @@ -245,7 +90,6 @@ static struct platform_driver msm_snd_apq8096_driver = { .remove = apq8096_platform_remove, .driver = { .name = "msm-snd-apq8096", - .owner = THIS_MODULE, .of_match_table = msm_snd_apq8096_dt_match, }, }; diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c new file mode 100644 index 000000000000..eb1b9da05dd4 --- /dev/null +++ b/sound/soc/qcom/common.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Linaro Limited. +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include <linux/module.h> +#include "common.h" + +int qcom_snd_parse_of(struct snd_soc_card *card) +{ + struct device_node *np; + struct device_node *codec = NULL; + struct device_node *platform = NULL; + struct device_node *cpu = NULL; + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + int ret, num_links; + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ret; + } + + /* DAPM routes */ + if (of_property_read_bool(dev->of_node, "audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "audio-routing"); + if (ret) + return ret; + } + + /* Populate links */ + num_links = of_get_child_count(dev->of_node); + + /* Allocate the DAI link array */ + card->dai_link = kcalloc(num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = num_links; + link = card->dai_link; + for_each_child_of_node(dev->of_node, np) { + cpu = of_get_child_by_name(np, "cpu"); + if (!cpu) { + dev_err(dev, "Can't find cpu DT node\n"); + ret = -EINVAL; + goto err; + } + + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!link->cpu_of_node) { + dev_err(card->dev, "error getting cpu phandle\n"); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); + if (ret) { + dev_err(card->dev, "error getting cpu dai name\n"); + goto err; + } + + platform = of_get_child_by_name(np, "platform"); + codec = of_get_child_by_name(np, "codec"); + if (codec && platform) { + link->platform_of_node = of_parse_phandle(platform, + "sound-dai", + 0); + if (!link->platform_of_node) { + dev_err(card->dev, "platform dai not found\n"); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + if (ret < 0) { + dev_err(card->dev, "codec dai not found\n"); + goto err; + } + link->no_pcm = 1; + link->ignore_pmdown_time = 1; + } else { + link->platform_of_node = link->cpu_of_node; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->codec_name = "snd-soc-dummy"; + link->dynamic = 1; + } + + link->ignore_suspend = 1; + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "error getting codec dai_link name\n"); + goto err; + } + + link->dpcm_playback = 1; + link->dpcm_capture = 1; + link->stream_name = link->name; + link++; + } + + return 0; +err: + of_node_put(cpu); + of_node_put(codec); + of_node_put(platform); + kfree(card->dai_link); + return ret; +} +EXPORT_SYMBOL(qcom_snd_parse_of); + +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h new file mode 100644 index 000000000000..f05c05b12bd7 --- /dev/null +++ b/sound/soc/qcom/common.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#ifndef __QCOM_SND_COMMON_H__ +#define __QCOM_SND_COMMON_H__ + +#include <sound/soc.h> + +int qcom_snd_parse_of(struct snd_soc_card *card); + +#endif diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 31fe78aa207f..d07271ea4c45 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -458,7 +458,7 @@ static irqreturn_t lpass_dma_interrupt_handler( return IRQ_NONE; } dev_warn(soc_runtime->dev, "xrun warning\n"); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(substream); ret = IRQ_HANDLED; } diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 9983c665a941..932c3ebfd252 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -64,7 +64,6 @@ struct q6adm { struct aprv2_ibasic_rsp_result_t result; struct mutex lock; wait_queue_head_t matrix_map_wait; - struct platform_device *pdev_routing; }; struct q6adm_cmd_device_open_v5 { @@ -588,7 +587,6 @@ EXPORT_SYMBOL_GPL(q6adm_close); static int q6adm_probe(struct apr_device *adev) { struct device *dev = &adev->dev; - struct device_node *dais_np; struct q6adm *adm; adm = devm_kzalloc(&adev->dev, sizeof(*adm), GFP_KERNEL); @@ -605,22 +603,12 @@ static int q6adm_probe(struct apr_device *adev) INIT_LIST_HEAD(&adm->copps_list); spin_lock_init(&adm->copps_list_lock); - dais_np = of_get_child_by_name(dev->of_node, "routing"); - if (dais_np) { - adm->pdev_routing = of_platform_device_create(dais_np, - "q6routing", dev); - of_node_put(dais_np); - } - - return 0; + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int q6adm_remove(struct apr_device *adev) { - struct q6adm *adm = dev_get_drvdata(&adev->dev); - - if (adm->pdev_routing) - of_platform_device_destroy(&adm->pdev_routing->dev, NULL); + of_platform_depopulate(&adev->dev); return 0; } diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 5002dd05bf27..60ff4a2d3577 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -4,7 +4,6 @@ #include <linux/err.h> #include <linux/init.h> -#include <linux/component.h> #include <linux/module.h> #include <linux/device.h> #include <linux/platform_device.h> @@ -81,7 +80,6 @@ static int q6slim_hw_params(struct snd_pcm_substream *substream, struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); struct q6afe_slim_cfg *slim = &dai_data->port_config[dai->id].slim; - slim->num_channels = params_channels(params); slim->sample_rate = params_rate(params); switch (params_format(params)) { @@ -315,6 +313,9 @@ static void q6afe_dai_shutdown(struct snd_pcm_substream *substream, struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); int rc; + if (!dai_data->is_port_started[dai->id]) + return; + rc = q6afe_port_stop(dai_data->port[dai->id]); if (rc < 0) dev_err(dai->dev, "fail to close AFE port (%d)\n", rc); @@ -382,23 +383,31 @@ static int q6slim_set_channel_map(struct snd_soc_dai *dai, struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id]; int i; - if (!rx_slot) { - pr_err("%s: rx slot not found\n", __func__); - return -EINVAL; - } + if (dai->id & 0x1) { + /* TX */ + if (!tx_slot) { + pr_err("%s: tx slot not found\n", __func__); + return -EINVAL; + } - for (i = 0; i < rx_num; i++) { - pcfg->slim.ch_mapping[i] = rx_slot[i]; - pr_debug("%s: find number of channels[%d] ch[%d]\n", - __func__, i, rx_slot[i]); - } + for (i = 0; i < tx_num; i++) + pcfg->slim.ch_mapping[i] = tx_slot[i]; + + pcfg->slim.num_channels = tx_num; + + + } else { + if (!rx_slot) { + pr_err("%s: rx slot not found\n", __func__); + return -EINVAL; + } - pcfg->slim.num_channels = rx_num; + for (i = 0; i < rx_num; i++) + pcfg->slim.ch_mapping[i] = rx_slot[i]; - pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__, - (dai->id - SLIMBUS_0_RX) / 2, rx_num, - pcfg->slim.ch_mapping[0], - pcfg->slim.ch_mapping[1]); + pcfg->slim.num_channels = rx_num; + + } return 0; } @@ -443,6 +452,14 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"Slimbus5 Playback", NULL, "SLIMBUS_5_RX"}, {"Slimbus6 Playback", NULL, "SLIMBUS_6_RX"}, + {"SLIMBUS_0_TX", NULL, "Slimbus Capture"}, + {"SLIMBUS_1_TX", NULL, "Slimbus1 Capture"}, + {"SLIMBUS_2_TX", NULL, "Slimbus2 Capture"}, + {"SLIMBUS_3_TX", NULL, "Slimbus3 Capture"}, + {"SLIMBUS_4_TX", NULL, "Slimbus4 Capture"}, + {"SLIMBUS_5_TX", NULL, "Slimbus5 Capture"}, + {"SLIMBUS_6_TX", NULL, "Slimbus6 Capture"}, + {"Primary MI2S Playback", NULL, "PRI_MI2S_RX"}, {"Secondary MI2S Playback", NULL, "SEC_MI2S_RX"}, {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"}, @@ -637,6 +654,24 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .rate_max = 192000, }, }, { + .name = "SLIMBUS_0_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_0_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + }, { .playback = { .stream_name = "Slimbus1 Playback", .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | @@ -655,6 +690,24 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, }, { + .name = "SLIMBUS_1_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_1_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus1 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + }, { .playback = { .stream_name = "Slimbus2 Playback", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | @@ -672,6 +725,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_2_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_2_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_2_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus2 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus3 Playback", @@ -690,6 +762,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_3_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_3_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_3_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus3 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus4 Playback", @@ -708,6 +799,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_4_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_4_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_4_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus4 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus5 Playback", @@ -726,6 +836,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_5_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_5_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_5_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus5 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Slimbus6 Playback", @@ -744,6 +873,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .id = SLIMBUS_6_RX, .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, + + }, { + .name = "SLIMBUS_6_TX", + .ops = &q6slim_ops, + .id = SLIMBUS_6_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .capture = { + .stream_name = "Slimbus6 Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, }, { .playback = { .stream_name = "Primary MI2S Playback", @@ -972,6 +1120,13 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture", @@ -1180,7 +1335,7 @@ static void of_q6afe_parse_dai_data(struct device *dev, int id, i, num_lines; ret = of_property_read_u32(node, "reg", &id); - if (ret || id > AFE_PORT_MAX) { + if (ret || id < 0 || id >= AFE_PORT_MAX) { dev_err(dev, "valid dai id not found:%d\n", ret); continue; } @@ -1249,11 +1404,12 @@ static void of_q6afe_parse_dai_data(struct device *dev, } } -static int q6afe_dai_bind(struct device *dev, struct device *master, void *data) +static int q6afe_dai_dev_probe(struct platform_device *pdev) { struct q6afe_dai_data *dai_data; + struct device *dev = &pdev->dev; - dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL); + dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; @@ -1261,41 +1417,22 @@ static int q6afe_dai_bind(struct device *dev, struct device *master, void *data) of_q6afe_parse_dai_data(dev, dai_data); - return snd_soc_register_component(dev, &q6afe_dai_component, + return devm_snd_soc_register_component(dev, &q6afe_dai_component, q6afe_dais, ARRAY_SIZE(q6afe_dais)); } -static void q6afe_dai_unbind(struct device *dev, struct device *master, - void *data) -{ - struct q6afe_dai_data *dai_data = dev_get_drvdata(dev); - - snd_soc_unregister_component(dev); - kfree(dai_data); -} - -static const struct component_ops q6afe_dai_comp_ops = { - .bind = q6afe_dai_bind, - .unbind = q6afe_dai_unbind, +static const struct of_device_id q6afe_dai_device_id[] = { + { .compatible = "qcom,q6afe-dais" }, + {}, }; - -static int q6afe_dai_dev_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &q6afe_dai_comp_ops); -} - -static int q6afe_dai_dev_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &q6afe_dai_comp_ops); - return 0; -} +MODULE_DEVICE_TABLE(of, q6afe_dai_device_id); static struct platform_driver q6afe_dai_platform_driver = { .driver = { .name = "q6afe-dai", + .of_match_table = of_match_ptr(q6afe_dai_device_id), }, .probe = q6afe_dai_dev_probe, - .remove = q6afe_dai_dev_remove, }; module_platform_driver(q6afe_dai_platform_driver); diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 01f43218984b..000775b4bba8 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -316,7 +316,6 @@ struct q6afe { struct mutex lock; struct list_head port_list; spinlock_t port_list_lock; - struct platform_device *pdev_dais; }; struct afe_port_cmd_device_start { @@ -515,6 +514,20 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { SLIMBUS_5_RX, 1, 1}, [SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX, SLIMBUS_6_RX, 1, 1}, + [SLIMBUS_0_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX, + SLIMBUS_0_TX, 0, 1}, + [SLIMBUS_1_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX, + SLIMBUS_1_TX, 0, 1}, + [SLIMBUS_2_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX, + SLIMBUS_2_TX, 0, 1}, + [SLIMBUS_3_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX, + SLIMBUS_3_TX, 0, 1}, + [SLIMBUS_4_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX, + SLIMBUS_4_TX, 0, 1}, + [SLIMBUS_5_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX, + SLIMBUS_5_TX, 0, 1}, + [SLIMBUS_6_TX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX, + SLIMBUS_6_TX, 0, 1}, [PRIMARY_MI2S_RX] = { AFE_PORT_ID_PRIMARY_MI2S_RX, PRIMARY_MI2S_RX, 1, 1}, [PRIMARY_MI2S_TX] = { AFE_PORT_ID_PRIMARY_MI2S_TX, @@ -777,7 +790,7 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) */ int q6afe_get_port_id(int index) { - if (index < 0 || index > AFE_PORT_MAX) + if (index < 0 || index >= AFE_PORT_MAX) return -EINVAL; return port_maps[index].port_id; @@ -1014,7 +1027,7 @@ int q6afe_port_stop(struct q6afe_port *port) port_id = port->id; index = port->token; - if (index < 0 || index > AFE_PORT_MAX) { + if (index < 0 || index >= AFE_PORT_MAX) { dev_err(afe->dev, "AFE port index[%d] invalid!\n", index); return -EINVAL; } @@ -1355,7 +1368,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) unsigned long flags; int cfg_type; - if (id < 0 || id > AFE_PORT_MAX) { + if (id < 0 || id >= AFE_PORT_MAX) { dev_err(dev, "AFE port token[%d] invalid!\n", id); return ERR_PTR(-EINVAL); } @@ -1373,6 +1386,13 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_MULTICHAN_HDMI_RX: cfg_type = AFE_PARAM_ID_HDMI_CONFIG; break; + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX: case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX: case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX: case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX: @@ -1438,7 +1458,6 @@ static int q6afe_probe(struct apr_device *adev) { struct q6afe *afe; struct device *dev = &adev->dev; - struct device_node *dais_np; afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) @@ -1453,22 +1472,12 @@ static int q6afe_probe(struct apr_device *adev) dev_set_drvdata(dev, afe); - dais_np = of_get_child_by_name(dev->of_node, "dais"); - if (dais_np) { - afe->pdev_dais = of_platform_device_create(dais_np, - "q6afe-dai", dev); - of_node_put(dais_np); - } - - return 0; + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int q6afe_remove(struct apr_device *adev) { - struct q6afe *afe = dev_get_drvdata(&adev->dev); - - if (afe->pdev_dais) - of_platform_device_destroy(&afe->pdev_dais->dev, NULL); + of_platform_depopulate(&adev->dev); return 0; } diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 349c6a883c63..9db9a2944ef2 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -7,7 +7,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/component.h> #include <sound/soc.h> #include <sound/soc.h> #include <sound/soc-dapm.h> @@ -390,7 +389,9 @@ static int q6asm_dai_close(struct snd_pcm_substream *substream) struct q6asm_dai_rtd *prtd = runtime->private_data; if (prtd->audio_client) { - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + if (prtd->state) + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); q6asm_audio_client_free(prtd->audio_client); @@ -561,14 +562,15 @@ static struct snd_soc_dai_driver q6asm_fe_dais[] = { Q6ASM_FEDAI_DRIVER(8), }; -static int q6asm_dai_bind(struct device *dev, struct device *master, void *data) +static int q6asm_dai_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct of_phandle_args args; struct q6asm_dai_data *pdata; int rc; - pdata = kzalloc(sizeof(struct q6asm_dai_data), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -580,43 +582,23 @@ static int q6asm_dai_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, pdata); - return snd_soc_register_component(dev, &q6asm_fe_dai_component, + return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, q6asm_fe_dais, ARRAY_SIZE(q6asm_fe_dais)); } -static void q6asm_dai_unbind(struct device *dev, struct device *master, - void *data) -{ - struct q6asm_dai_data *pdata = dev_get_drvdata(dev); - - snd_soc_unregister_component(dev); - - kfree(pdata); - -} -static const struct component_ops q6asm_dai_comp_ops = { - .bind = q6asm_dai_bind, - .unbind = q6asm_dai_unbind, +static const struct of_device_id q6asm_dai_device_id[] = { + { .compatible = "qcom,q6asm-dais" }, + {}, }; - -static int q6asm_dai_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &q6asm_dai_comp_ops); -} - -static int q6asm_dai_dev_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &q6asm_dai_comp_ops); - return 0; -} +MODULE_DEVICE_TABLE(of, q6asm_dai_device_id); static struct platform_driver q6asm_dai_platform_driver = { .driver = { .name = "q6asm-dai", + .of_match_table = of_match_ptr(q6asm_dai_device_id), }, .probe = q6asm_dai_probe, - .remove = q6asm_dai_dev_remove, }; module_platform_driver(q6asm_dai_platform_driver); diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 530852385cad..2b2c7233bb5f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -174,10 +174,8 @@ struct q6asm { struct device *dev; struct q6core_svc_api_info ainfo; wait_queue_head_t mem_wait; - struct platform_device *pcmdev; spinlock_t slock; struct audio_client *session[MAX_SESSIONS + 1]; - struct platform_device *pdev_dais; }; struct audio_client { @@ -1344,7 +1342,6 @@ EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); static int q6asm_probe(struct apr_device *adev) { struct device *dev = &adev->dev; - struct device_node *dais_np; struct q6asm *q6asm; q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL); @@ -1359,22 +1356,12 @@ static int q6asm_probe(struct apr_device *adev) spin_lock_init(&q6asm->slock); dev_set_drvdata(dev, q6asm); - dais_np = of_get_child_by_name(dev->of_node, "dais"); - if (dais_np) { - q6asm->pdev_dais = of_platform_device_create(dais_np, - "q6asm-dai", dev); - of_node_put(dais_np); - } - - return 0; + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int q6asm_remove(struct apr_device *adev) { - struct q6asm *q6asm = dev_get_drvdata(&adev->dev); - - if (q6asm->pdev_dais) - of_platform_device_destroy(&q6asm->pdev_dais->dev, NULL); + of_platform_depopulate(&adev->dev); return 0; } diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 593f66b8622f..dc94c5c53788 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -8,7 +8,6 @@ #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/bitops.h> -#include <linux/component.h> #include <linux/mutex.h> #include <linux/of_device.h> #include <linux/slab.h> @@ -68,6 +67,13 @@ { mix_name, "SEC_MI2S_TX", "SEC_MI2S_TX" }, \ { mix_name, "QUAT_MI2S_TX", "QUAT_MI2S_TX" }, \ { mix_name, "TERT_MI2S_TX", "TERT_MI2S_TX" }, \ + { mix_name, "SLIMBUS_0_TX", "SLIMBUS_0_TX" }, \ + { mix_name, "SLIMBUS_1_TX", "SLIMBUS_1_TX" }, \ + { mix_name, "SLIMBUS_2_TX", "SLIMBUS_2_TX" }, \ + { mix_name, "SLIMBUS_3_TX", "SLIMBUS_3_TX" }, \ + { mix_name, "SLIMBUS_4_TX", "SLIMBUS_4_TX" }, \ + { mix_name, "SLIMBUS_5_TX", "SLIMBUS_5_TX" }, \ + { mix_name, "SLIMBUS_6_TX", "SLIMBUS_6_TX" }, \ { mix_name, "PRIMARY_TDM_TX_0", "PRIMARY_TDM_TX_0"}, \ { mix_name, "PRIMARY_TDM_TX_1", "PRIMARY_TDM_TX_1"}, \ { mix_name, "PRIMARY_TDM_TX_2", "PRIMARY_TDM_TX_2"}, \ @@ -122,6 +128,27 @@ SOC_SINGLE_EXT("QUAT_MI2S_TX", QUATERNARY_MI2S_TX, \ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_0_TX", SLIMBUS_0_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_1_TX", SLIMBUS_1_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_2_TX", SLIMBUS_2_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_3_TX", SLIMBUS_3_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_4_TX", SLIMBUS_4_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_5_TX", SLIMBUS_5_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SLIMBUS_6_TX", SLIMBUS_6_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("PRIMARY_TDM_TX_0", PRIMARY_TDM_TX_0, \ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), \ @@ -310,7 +337,7 @@ int q6routing_stream_open(int fedai_id, int perf_mode, session->channels, topology, perf_mode, session->bits_per_sample, 0, 0); - if (!copp) { + if (IS_ERR_OR_NULL(copp)) { mutex_unlock(&routing_data->lock); return -EINVAL; } @@ -899,7 +926,7 @@ static int routing_hw_params(struct snd_pcm_substream *substream, else path_type = ADM_PATH_LIVE_REC; - if (be_id > AFE_MAX_PORTS) + if (be_id >= AFE_MAX_PORTS) return -EINVAL; session = &data->port_data[be_id]; @@ -949,9 +976,10 @@ static const struct snd_soc_component_driver msm_soc_routing_component = { .num_dapm_routes = ARRAY_SIZE(intercon), }; -static int q6routing_dai_bind(struct device *dev, struct device *master, - void *data) +static int q6pcm_routing_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + routing_data = kzalloc(sizeof(*routing_data), GFP_KERNEL); if (!routing_data) return -ENOMEM; @@ -961,41 +989,28 @@ static int q6routing_dai_bind(struct device *dev, struct device *master, mutex_init(&routing_data->lock); dev_set_drvdata(dev, routing_data); - return snd_soc_register_component(dev, &msm_soc_routing_component, + return devm_snd_soc_register_component(dev, &msm_soc_routing_component, NULL, 0); } -static void q6routing_dai_unbind(struct device *dev, struct device *master, - void *d) +static int q6pcm_routing_remove(struct platform_device *pdev) { - struct msm_routing_data *data = dev_get_drvdata(dev); - - snd_soc_unregister_component(dev); - - kfree(data); - + kfree(routing_data); routing_data = NULL; -} - -static const struct component_ops q6routing_dai_comp_ops = { - .bind = q6routing_dai_bind, - .unbind = q6routing_dai_unbind, -}; -static int q6pcm_routing_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &q6routing_dai_comp_ops); -} - -static int q6pcm_routing_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &q6routing_dai_comp_ops); return 0; } +static const struct of_device_id q6pcm_routing_device_id[] = { + { .compatible = "qcom,q6adm-routing" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id); + static struct platform_driver q6pcm_routing_platform_driver = { .driver = { .name = "q6routing", + .of_match_table = of_match_ptr(q6pcm_routing_device_id), }, .probe = q6pcm_routing_probe, .remove = q6pcm_routing_remove, diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c new file mode 100644 index 000000000000..2a781d87ee65 --- /dev/null +++ b/sound/soc/qcom/sdm845.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "common.h" +#include "qdsp6/q6afe.h" + +#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 24576000 +#define DEFAULT_BCLK_RATE 12288000 + +struct sdm845_snd_data { + struct snd_soc_card *card; + uint32_t pri_mi2s_clk_count; + uint32_t quat_tdm_clk_count; +}; + +static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + +static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 32; + break; + default: + dev_err(rtd->dev, "%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + channels = params_channels(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x3, + 8, slot_width); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, tdm_slot_offset); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0, + 8, slot_width); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + tdm_slot_offset, 0, NULL); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + switch (cpu_dai->id) { + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + ret = sdm845_tdm_snd_hw_params(substream, params); + break; + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + return ret; +} + +static int sdm845_snd_startup(struct snd_pcm_substream *substream) +{ + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + if (++(data->pri_mi2s_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_MCLK_1, + DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + snd_soc_dai_set_fmt(cpu_dai, fmt); + break; + + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + if (++(data->quat_tdm_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, + DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + break; + + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + return 0; +} + +static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + if (--(data->pri_mi2s_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_MCLK_1, + 0, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + 0, SNDRV_PCM_STREAM_PLAYBACK); + }; + break; + + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + if (--(data->quat_tdm_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, + 0, SNDRV_PCM_STREAM_PLAYBACK); + } + break; + + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } +} + +static struct snd_soc_ops sdm845_be_ops = { + .hw_params = sdm845_snd_hw_params, + .startup = sdm845_snd_startup, + .shutdown = sdm845_snd_shutdown, +}; + +static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static void sdm845_add_be_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link = card->dai_link; + int i, num_links = card->num_links; + + for (i = 0; i < num_links; i++) { + if (link->no_pcm == 1) { + link->ops = &sdm845_be_ops; + link->be_hw_params_fixup = sdm845_be_hw_params_fixup; + } + link++; + } +} + +static int sdm845_snd_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct sdm845_snd_data *data; + struct device *dev = &pdev->dev; + int ret; + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + /* Allocate the private data */ + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto data_alloc_fail; + } + + card->dev = dev; + dev_set_drvdata(dev, card); + ret = qcom_snd_parse_of(card); + if (ret) { + dev_err(dev, "Error parsing OF data\n"); + goto parse_dt_fail; + } + + data->card = card; + snd_soc_card_set_drvdata(card, data); + + sdm845_add_be_ops(card); + ret = snd_soc_register_card(card); + if (ret) { + dev_err(dev, "Sound card registration failed\n"); + goto register_card_fail; + } + return ret; + +register_card_fail: + kfree(card->dai_link); +parse_dt_fail: + kfree(data); +data_alloc_fail: + kfree(card); + return ret; +} + +static int sdm845_snd_platform_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = dev_get_drvdata(&pdev->dev); + struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + kfree(card->dai_link); + kfree(data); + kfree(card); + return 0; +} + +static const struct of_device_id sdm845_snd_device_id[] = { + { .compatible = "qcom,sdm845-sndcard" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdm845_snd_device_id); + +static struct platform_driver sdm845_snd_driver = { + .probe = sdm845_snd_platform_probe, + .remove = sdm845_snd_platform_remove, + .driver = { + .name = "msm-snd-sdm845", + .of_match_table = sdm845_snd_device_id, + }, +}; +module_platform_driver(sdm845_snd_driver); + +MODULE_DESCRIPTION("sdm845 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 05b078e7b87f..65e814d46006 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-pcm-objs := rockchip_pcm.o snd-soc-rockchip-pdm-objs := rockchip_pdm.o snd-soc-rockchip-spdif-objs := rockchip_spdif.o -obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 950823d69e9c..60d43d53a8f5 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -22,6 +22,7 @@ #include <sound/dmaengine_pcm.h> #include "rockchip_i2s.h" +#include "rockchip_pcm.h" #define DRV_NAME "rockchip-i2s" @@ -674,7 +675,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_suspend; } - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + ret = rockchip_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); return ret; diff --git a/sound/soc/rockchip/rockchip_pcm.c b/sound/soc/rockchip/rockchip_pcm.c new file mode 100644 index 000000000000..f77538319221 --- /dev/null +++ b/sound/soc/rockchip/rockchip_pcm.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "rockchip_pcm.h" + +static const struct snd_pcm_hardware snd_rockchip_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 52, + .buffer_bytes_max = 64 * 1024, + .fifo_size = 32, +}; + +static const struct snd_dmaengine_pcm_config rk_dmaengine_pcm_config = { + .pcm_hardware = &snd_rockchip_hardware, + .prealloc_buffer_size = 32 * 1024, +}; + +int rockchip_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &rk_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); +} +EXPORT_SYMBOL_GPL(rockchip_pcm_platform_register); + +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/rockchip_pcm.h b/sound/soc/rockchip/rockchip_pcm.h new file mode 100644 index 000000000000..d6c36115c60a --- /dev/null +++ b/sound/soc/rockchip/rockchip_pcm.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ROCKCHIP_PCM_H +#define _ROCKCHIP_PCM_H + +int rockchip_pcm_platform_register(struct device *dev); + +#endif diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 4db4fd56db35..881c32498808 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -181,7 +181,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) if (!rk_dailink.cpu_of_node) { dev_err(&pdev->dev, "Property 'rockchip,i2s-controller' missing or invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto put_codec_of_node; } rk_dailink.platform_of_node = rk_dailink.cpu_of_node; @@ -190,17 +191,36 @@ static int snd_rk_mc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Soc parse card name failed %d\n", ret); - return ret; + goto put_cpu_of_node; } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "Soc register card failed %d\n", ret); - return ret; + goto put_cpu_of_node; } return ret; + +put_cpu_of_node: + of_node_put(rk_dailink.cpu_of_node); + rk_dailink.cpu_of_node = NULL; +put_codec_of_node: + of_node_put(rk_dailink.codec_of_node); + rk_dailink.codec_of_node = NULL; + + return ret; +} + +static int snd_rk_mc_remove(struct platform_device *pdev) +{ + of_node_put(rk_dailink.cpu_of_node); + rk_dailink.cpu_of_node = NULL; + of_node_put(rk_dailink.codec_of_node); + rk_dailink.codec_of_node = NULL; + + return 0; } static const struct of_device_id rockchip_rt5645_of_match[] = { @@ -212,6 +232,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match); static struct platform_driver snd_rk_mc_driver = { .probe = snd_rk_mc_probe, + .remove = snd_rk_mc_remove, .driver = { .name = DRV_NAME, .pm = &snd_soc_pm_ops, diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f914ed45db7d..d6c62aa13041 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -710,6 +710,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, switch (params_channels(params)) { case 6: val |= MOD_DC2_EN; + /* fall through */ case 4: val |= MOD_DC1_EN; break; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 0ae0800bf3a8..dc20f0f7080a 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 menu "SoC Audio support for Renesas SoCs" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index 2dc3b762fdd9..922fb6aa3ed1 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -1,16 +1,14 @@ -/* - * SH7760 ("camelot") DMABRG audio DMA unit support - * - * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * licensed under the terms outlined in the file COPYING at the root - * of the linux kernel sources. - * - * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which - * trigger an interrupt when one half of the programmed transfer size - * has been xmitted. - * - * FIXME: little-endian only for now - */ +// SPDX-License-Identifier: GPL-2.0 +// +// SH7760 ("camelot") DMABRG audio DMA unit support +// +// Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> +// +// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which +// trigger an interrupt when one half of the programmed transfer size +// has been xmitted. +// +// FIXME: little-endian only for now #include <linux/module.h> #include <linux/gfp.h> @@ -341,6 +339,6 @@ static struct platform_driver sh7760_pcm_driver = { module_platform_driver(sh7760_pcm_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3bae06dd121f..aa7e902f0c02 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1,16 +1,12 @@ -/* - * Fifo-attached Serial Interface (FSI) support for SH7724 - * - * Copyright (C) 2009 Renesas Solutions Corp. - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * Based on ssi.c - * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Fifo-attached Serial Interface (FSI) support for SH7724 +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Kuninori Morimoto <morimoto.kuninori@renesas.com> +// +// Based on ssi.c +// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> #include <linux/delay.h> #include <linux/dma-mapping.h> diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 624aaf569fef..c2b496398e6b 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -1,13 +1,11 @@ -/* - * Hitachi Audio Controller (AC97) support for SH7760/SH7780 - * - * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * licensed under the terms outlined in the file COPYING at the root - * of the linux kernel sources. - * - * dont forget to set IPSEL/OMSEL register bits (in your board code) to - * enable HAC output pins! - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Hitachi Audio Controller (AC97) support for SH7760/SH7780 +// +// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> +// +// dont forget to set IPSEL/OMSEL register bits (in your board code) to +// enable HAC output pins! /* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only * the FIRST can be used since ASoC does not pass any information to the @@ -343,6 +341,6 @@ static struct platform_driver hac_pcm_driver = { module_platform_driver(hac_pcm_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index ecb057ff9fbb..8739c9f60672 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -1,12 +1,8 @@ -/* - * ALSA SoC driver for Migo-R - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC driver for Migo-R +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> #include <linux/clkdev.h> #include <linux/device.h> diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 9c3d5aed99d1..5d1ff8ef26f9 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 4672688cac32..3a3064dda57f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -1,12 +1,9 @@ -/* - * Helper routines for R-Car sound ADG. - * - * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Helper routines for R-Car sound ADG. +// +// Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/clk-provider.h> #include "rsnd.h" diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index 5900fb535a2b..cc191cd5fb82 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -1,13 +1,10 @@ -/* - * Renesas R-Car CMD support - * - * Copyright (C) 2015 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car CMD support +// +// Copyright (C) 2015 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include "rsnd.h" struct rsnd_cmd { @@ -89,7 +86,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, cmd_case[rsnd_mod_id(src)] << 16; } - dev_dbg(dev, "ctu/mix path = 0x%08x", data); + dev_dbg(dev, "ctu/mix path = 0x%08x\n", data); rsnd_mod_write(mod, CMD_ROUTE_SLCT, data); rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1); diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f237002180c0..f8425d8b44d2 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1,16 +1,12 @@ -/* - * Renesas R-Car SRU/SCU/SSIU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * Based on fsi.c - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SRU/SCU/SSIU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// Based on fsi.c +// Kuninori Morimoto <morimoto.kuninori@renesas.com> /* * Renesas R-Car sound device structure @@ -552,6 +548,15 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) return priv->rdai + id; } +static struct snd_soc_dai_driver +*rsnd_daidrv_get(struct rsnd_priv *priv, int id) +{ + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) + return NULL; + + return priv->daidrv + id; +} + #define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai) static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) { @@ -1037,7 +1042,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, int io_i; rdai = rsnd_rdai_get(priv, dai_i); - drv = priv->daidrv + dai_i; + drv = rsnd_daidrv_get(priv, dai_i); io_playback = &rdai->playback; io_capture = &rdai->capture; @@ -1085,6 +1090,12 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, of_node_put(capture); } + if (rsnd_ssi_is_pin_sharing(io_capture) || + rsnd_ssi_is_pin_sharing(io_playback)) { + /* should have symmetric_rates if pin sharing */ + drv->symmetric_rates = 1; + } + dev_dbg(dev, "%s (%s/%s)\n", rdai->name, rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); @@ -1606,7 +1617,7 @@ static struct platform_driver rsnd_driver = { }; module_platform_driver(rsnd_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas R-Car audio driver"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 83be7d3ae0a8..6a55aa753003 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -1,12 +1,9 @@ -/* - * ctu.c - * - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// ctu.c +// +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include "rsnd.h" #define CTU_NAME_SIZE 16 diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index ef82b94d038b..fe63ef8600d0 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -1,13 +1,10 @@ -/* - * Renesas R-Car Audio DMAC support - * - * Copyright (C) 2015 Renesas Electronics Corp. - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car Audio DMAC support +// +// Copyright (C) 2015 Renesas Electronics Corp. +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include <linux/delay.h> #include <linux/of_dma.h> #include "rsnd.h" diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index ca1780e0b830..2b16e0ce6bc5 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -1,13 +1,9 @@ -/* - * Renesas R-Car DVC support - * - * Copyright (C) 2014 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car DVC support +// +// Copyright (C) 2014 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * Playback Volume diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 25642e92dae0..0230301fe078 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -1,13 +1,9 @@ -/* - * Renesas R-Car Gen1 SRU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car Gen1 SRU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * #define DEBUG diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 1881b2de9126..8e3b57eaa708 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -1,12 +1,8 @@ -/* - * mix.c - * - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// mix.c +// +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * CTUn MIXn diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 6d7280d2d9be..96d93330b1e1 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -1,13 +1,10 @@ -/* - * Renesas R-Car - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #ifndef RSND_H #define RSND_H diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 6c72d1a81cf5..beccfbac7581 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -1,13 +1,9 @@ -/* - * Renesas R-Car SRC support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SRC support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> /* * you can enable below define if you don't need diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 6e1166ec24a0..8304e4ec9242 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -1,16 +1,12 @@ -/* - * Renesas R-Car SSIU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * Based on fsi.c - * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SSIU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +// +// Based on fsi.c +// Kuninori Morimoto <morimoto.kuninori@renesas.com> /* * you can enable below define if you don't need @@ -37,6 +33,7 @@ #define CHNL_4 (1 << 22) /* Channels */ #define CHNL_6 (2 << 22) /* Channels */ #define CHNL_8 (3 << 22) /* Channels */ +#define DWL_MASK (7 << 19) /* Data Word Length mask */ #define DWL_8 (0 << 19) /* Data Word Length */ #define DWL_16 (1 << 19) /* Data Word Length */ #define DWL_18 (2 << 19) /* Data Word Length */ @@ -353,21 +350,18 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr_own; - u32 cr_mode; - u32 wsr; + u32 cr_own = ssi->cr_own; + u32 cr_mode = ssi->cr_mode; + u32 wsr = ssi->wsr; int is_tdm; - if (rsnd_ssi_is_parent(mod, io)) - return; - is_tdm = rsnd_runtime_is_ssi_tdm(io); /* * always use 32bit system word. * see also rsnd_ssi_master_clk_enable() */ - cr_own = FORCE | SWL_32; + cr_own |= FORCE | SWL_32; if (rdai->bit_clk_inv) cr_own |= SCKP; @@ -377,9 +371,18 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, cr_own |= SDTA; if (rdai->sys_delay) cr_own |= DEL; + + /* + * We shouldn't exchange SWSP after running. + * This means, parent needs to care it. + */ + if (rsnd_ssi_is_parent(mod, io)) + goto init_end; + if (rsnd_io_is_play(io)) cr_own |= TRMD; + cr_own &= ~DWL_MASK; switch (snd_pcm_format_width(runtime->format)) { case 16: cr_own |= DWL_16; @@ -406,7 +409,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, wsr |= WS_MODE; cr_own |= CHNL_8; } - +init_end: ssi->cr_own = cr_own; ssi->cr_mode = cr_mode; ssi->wsr = wsr; @@ -470,15 +473,18 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return -EIO; } - if (!rsnd_ssi_is_parent(mod, io)) - ssi->cr_own = 0; - rsnd_ssi_master_clk_stop(mod, io); rsnd_mod_power_off(mod); ssi->usrcnt--; + if (!ssi->usrcnt) { + ssi->cr_own = 0; + ssi->cr_mode = 0; + ssi->wsr = 0; + } + return 0; } @@ -1055,9 +1061,10 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + if (!mod) + return 0; - return !!(rsnd_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE)); + return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE)); } static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 47bdba9fc582..016fbf5ac242 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -1,12 +1,9 @@ -/* - * Renesas R-Car SSIU support - * - * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SSIU support +// +// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + #include "rsnd.h" #define SSIU_NAME "ssiu" diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c index 4a3568a9bf59..4bb4c13cf860 100644 --- a/sound/soc/sh/sh7760-ac97.c +++ b/sound/soc/sh/sh7760-ac97.c @@ -1,10 +1,8 @@ -/* - * Generic AC97 sound support for SH7760 - * - * (c) 2007 Manuel Lauss - * - * Licensed under the GPLv2. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Generic AC97 sound support for SH7760 +// +// (c) 2007 Manuel Lauss #include <linux/module.h> #include <linux/moduleparam.h> @@ -68,6 +66,6 @@ static void __exit sh7760_ac97_exit(void) module_init(sh7760_ac97_init); module_exit(sh7760_ac97_exit); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h index 6088d627c0e4..63a508fdfe78 100644 --- a/sound/soc/sh/siu.h +++ b/sound/soc/sh/siu.h @@ -1,23 +1,9 @@ -/* - * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> +// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> #ifndef SIU_H #define SIU_H diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index ee2211635e92..f2a386fcd92e 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -1,23 +1,9 @@ -/* - * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> +// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> #include <linux/delay.h> #include <linux/firmware.h> diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 172909570ed5..e263757e4a69 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -1,23 +1,10 @@ -/* - * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. - * - * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> +// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> + #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c index 89ed1b107ac5..8125fa3840b6 100644 --- a/sound/soc/sh/ssi.c +++ b/sound/soc/sh/ssi.c @@ -1,14 +1,11 @@ -/* - * Serial Sound Interface (I2S) support for SH7760/SH7780 - * - * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> - * - * licensed under the terms outlined in the file COPYING at the root - * of the linux kernel sources. - * - * dont forget to set IPSEL/OMSEL register bits (in your board code) to - * enable SSI output pins! - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Serial Sound Interface (I2S) support for SH7760/SH7780 +// +// Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> +// +// dont forget to set IPSEL/OMSEL register bits (in your board code) to +// enable SSI output pins! /* * LIMITATIONS: @@ -400,6 +397,6 @@ static struct platform_driver sh4_ssi_driver = { module_platform_driver(sh4_ssi_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver"); MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c index 77e7dcf969d0..d70fcd4a1adf 100644 --- a/sound/soc/sirf/sirf-usp.c +++ b/sound/soc/sirf/sirf-usp.c @@ -370,10 +370,9 @@ static int sirf_usp_pcm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, usp); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap(&pdev->dev, mem_res->start, - resource_size(mem_res)); - if (base == NULL) - return -ENOMEM; + base = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(base)) + return PTR_ERR(base); usp->regmap = devm_regmap_init_mmio(&pdev->dev, base, &sirf_usp_regmap_config); if (IS_ERR(usp->regmap)) diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 3f424f214bca..c086786e4471 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -1,20 +1,15 @@ -/* - * soc-ac97.c -- ALSA SoC Audio Layer AC97 support - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * with code, comments and ideas from :- - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-ac97.c -- ALSA SoC Audio Layer AC97 support +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// with code, comments and ideas from :- +// Richard Purdie <richard@openedhand.com> #include <linux/ctype.h> #include <linux/delay.h> diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index 3d7e1ff79139..b8e72b52db30 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -1,18 +1,8 @@ -/* - * soc-apci.c - support for ACPI enumeration. - * - * Copyright (c) 2013-15, Intel Corporation. - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// soc-apci.c - support for ACPI enumeration. +// +// Copyright (c) 2013-15, Intel Corporation. #include <sound/soc-acpi.h> diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index e095115fa9f9..409d082e80d1 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -1,18 +1,12 @@ -/* - * soc-compress.c -- ALSA SoC Compress - * - * Copyright (C) 2012 Intel Corp. - * - * Authors: Namarta Kohli <namartax.kohli@intel.com> - * Ramesh Babu K V <ramesh.babu@linux.intel.com> - * Vinod Koul <vinod.koul@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-compress.c -- ALSA SoC Compress +// +// Copyright (C) 2012 Intel Corp. +// +// Authors: Namarta Kohli <namartax.kohli@intel.com> +// Ramesh Babu K V <ramesh.babu@linux.intel.com> +// Vinod Koul <vinod.koul@linux.intel.com> #include <linux/kernel.h> #include <linux/init.h> @@ -146,6 +140,30 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) stream = SNDRV_PCM_STREAM_CAPTURE; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + fe->dpcm[stream].runtime = fe_substream->runtime; + + ret = dpcm_path_get(fe, stream, &list); + if (ret < 0) + goto be_err; + else if (ret == 0) + dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", + fe->dai_link->name, stream ? "capture" : "playback"); + /* calculate valid and active FE <-> BE dpcms */ + dpcm_process_paths(fe, stream, &list, 1); + fe->dpcm[stream].runtime = fe_substream->runtime; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_startup(fe, stream); + if (ret < 0) { + /* clean up all links */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + fe->dpcm[stream].runtime = NULL; + goto out; + } if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); @@ -159,7 +177,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) ret = soc_compr_components_open(cstream, &component); if (ret < 0) - goto machine_err; + goto open_err; if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ret = fe->dai_link->compr_ops->startup(cstream); @@ -170,31 +188,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } } - fe->dpcm[stream].runtime = fe_substream->runtime; - - ret = dpcm_path_get(fe, stream, &list); - if (ret < 0) - goto fe_err; - else if (ret == 0) - dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", - fe->dai_link->name, stream ? "capture" : "playback"); - - /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); - - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; - - ret = dpcm_be_dai_startup(fe, stream); - if (ret < 0) { - /* clean up all links */ - list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) - dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; - - dpcm_be_disconnect(fe, stream); - fe->dpcm[stream].runtime = NULL; - goto path_err; - } - dpcm_clear_pending_state(fe, stream); dpcm_path_put(&list); @@ -207,17 +200,14 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) return 0; -path_err: - dpcm_path_put(&list); -fe_err: - if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) - fe->dai_link->compr_ops->shutdown(cstream); machine_err: soc_compr_components_free(cstream, component); - +open_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: + dpcm_path_put(&list); +be_err: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; mutex_unlock(&fe->card->mutex); return ret; @@ -557,6 +547,24 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + /* + * Create an empty hw_params for the BE as the machine driver must + * fix this up to match DSP decoder and ASRC configuration. + * I.e. machine driver fixup for compressed BE is mandatory. + */ + memset(&fe->dpcm[fe_substream->stream].hw_params, 0, + sizeof(struct snd_pcm_hw_params)); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_params(fe, stream); + if (ret < 0) + goto out; + + ret = dpcm_be_dai_prepare(fe, stream); + if (ret < 0) + goto out; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { ret = cpu_dai->driver->cops->set_params(cstream, params, cpu_dai); if (ret < 0) @@ -583,24 +591,6 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - /* - * Create an empty hw_params for the BE as the machine driver must - * fix this up to match DSP decoder and ASRC configuration. - * I.e. machine driver fixup for compressed BE is mandatory. - */ - memset(&fe->dpcm[fe_substream->stream].hw_params, 0, - sizeof(struct snd_pcm_hw_params)); - - fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; - - ret = dpcm_be_dai_hw_params(fe, stream); - if (ret < 0) - goto out; - - ret = dpcm_be_dai_prepare(fe, stream); - if (ret < 0) - goto out; - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4663de3cf495..9cfe10d8040c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1,26 +1,21 @@ -/* - * soc-core.c -- ALSA SoC Audio Layer - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * with code, comments and ideas from :- - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * TODO: - * o Add hw rules to enforce rates, etc. - * o More testing with other codecs/machines. - * o Add more codecs and platforms to ensure good API coverage. - * o Support TDM on PCM and I2S - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-core.c -- ALSA SoC Audio Layer +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// with code, comments and ideas from :- +// Richard Purdie <richard@openedhand.com> +// +// TODO: +// o Add hw rules to enforce rates, etc. +// o More testing with other codecs/machines. +// o Add more codecs and platforms to ensure good API coverage. +// o Support TDM on PCM and I2S #include <linux/module.h> #include <linux/moduleparam.h> @@ -533,6 +528,7 @@ int snd_soc_suspend(struct device *dev) "ASoC: idle_bias_off CODEC on over suspend\n"); break; } + /* fall through */ case SND_SOC_BIAS_OFF: if (component->driver->suspend) @@ -852,6 +848,9 @@ static int soc_bind_dai_link(struct snd_soc_card *card, const char *platform_name; int i; + if (dai_link->ignore) + return 0; + dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); if (soc_is_dai_link_bound(card, dai_link)) { @@ -1195,15 +1194,27 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); +static void soc_set_of_name_prefix(struct snd_soc_component *component) +{ + struct device_node *component_of_node = component->dev->of_node; + const char *str; + int ret; + + if (!component_of_node && component->dev->parent) + component_of_node = component->dev->parent->of_node; + + ret = of_property_read_string(component_of_node, "sound-name-prefix", + &str); + if (!ret) + component->name_prefix = str; +} + static void soc_set_name_prefix(struct snd_soc_card *card, struct snd_soc_component *component) { int i; - if (card->codec_conf == NULL) - return; - - for (i = 0; i < card->num_configs; i++) { + for (i = 0; i < card->num_configs && card->codec_conf; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; struct device_node *component_of_node = component->dev->of_node; @@ -1215,8 +1226,14 @@ static void soc_set_name_prefix(struct snd_soc_card *card, if (map->dev_name && strcmp(component->name, map->dev_name)) continue; component->name_prefix = map->name_prefix; - break; + return; } + + /* + * If there is no configuration table or no match in the table, + * check if a prefix is provided in the node + */ + soc_set_of_name_prefix(component); } static int soc_probe_component(struct snd_soc_card *card, @@ -1461,7 +1478,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, { struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + int i, ret, num; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); @@ -1507,9 +1526,28 @@ static int soc_probe_link_dais(struct snd_soc_card *card, soc_dpcm_debugfs_add(rtd); #endif + num = rtd->num; + + /* + * most drivers will register their PCMs using DAI link ordering but + * topology based drivers can use the DAI link id field to set PCM + * device number and then use rtd + a base offset of the BEs. + */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->use_dai_pcm_id) + continue; + + if (rtd->dai_link->no_pcm) + num += component->driver->be_pcm_base; + else + num = rtd->dai_link->id; + } + if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = cpu_dai->driver->compress_new(rtd, rtd->num); + ret = cpu_dai->driver->compress_new(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); @@ -1519,7 +1557,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, if (!dai_link->params) { /* create the pcm */ - ret = soc_new_pcm(rtd, rtd->num); + ret = soc_new_pcm(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); @@ -1846,6 +1884,74 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); #endif /* CONFIG_DMI */ +static void soc_check_tplg_fes(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + const struct snd_soc_component_driver *comp_drv; + struct snd_soc_dai_link *dai_link; + int i; + + list_for_each_entry(component, &component_list, list) { + + /* does this component override FEs ? */ + if (!component->driver->ignore_machine) + continue; + + /* for this machine ? */ + if (strcmp(component->driver->ignore_machine, + card->dev->driver->name)) + continue; + + /* machine matches, so override the rtd data */ + for (i = 0; i < card->num_links; i++) { + + dai_link = &card->dai_link[i]; + + /* ignore this FE */ + if (dai_link->dynamic) { + dai_link->ignore = true; + continue; + } + + dev_info(card->dev, "info: override FE DAI link %s\n", + card->dai_link[i].name); + + /* override platform component */ + dai_link->platform_name = component->name; + + /* convert non BE into BE */ + dai_link->no_pcm = 1; + + /* override any BE fixups */ + dai_link->be_hw_params_fixup = + component->driver->be_hw_params_fixup; + + /* most BE links don't set stream name, so set it to + * dai link name if it's NULL to help bind widgets. + */ + if (!dai_link->stream_name) + dai_link->stream_name = dai_link->name; + } + + /* Inform userspace we are using alternate topology */ + if (component->driver->topology_name_prefix) { + + /* topology shortname created ? */ + if (!card->topology_shortname_created) { + comp_drv = component->driver; + + snprintf(card->topology_shortname, 32, "%s-%s", + comp_drv->topology_name_prefix, + card->name); + card->topology_shortname_created = true; + } + + /* use topology shortname */ + card->name = card->topology_shortname; + } + } +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -1855,6 +1961,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + /* check whether any platform is ignore machine FE and using topology */ + soc_check_tplg_fes(card); + /* bind DAIs */ for (i = 0; i < card->num_links; i++) { ret = soc_bind_dai_link(card, &card->dai_link[i]); @@ -2523,6 +2632,28 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); /** + * snd_soc_dai_get_channel_map - Get DAI audio channel map + * @dai: DAI + * @tx_num: how many TX channels + * @tx_slot: pointer to an array which imply the TX slot number channel + * 0~num-1 uses + * @rx_num: how many RX channels + * @rx_slot: pointer to an array which imply the RX slot number channel + * 0~num-1 uses + */ +int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + if (dai->driver->ops->get_channel_map) + return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, + rx_num, rx_slot); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); + +/** * snd_soc_dai_set_tristate - configure DAI system or master clock. * @dai: DAI * @tristate: tristate enable @@ -3258,9 +3389,9 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); -static int snd_soc_of_get_slot_mask(struct device_node *np, - const char *prop_name, - unsigned int *mask) +int snd_soc_of_get_slot_mask(struct device_node *np, + const char *prop_name, + unsigned int *mask) { u32 val; const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); @@ -3275,6 +3406,7 @@ static int snd_soc_of_get_slot_mask(struct device_node *np, return val; } +EXPORT_SYMBOL_GPL(snd_soc_of_get_slot_mask); int snd_soc_of_parse_tdm_slot(struct device_node *np, unsigned int *tx_mask, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 229c12349803..7e96793050c9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1,27 +1,21 @@ -/* - * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * Features: - * o Changes power status of internal codec blocks depending on the - * dynamic configuration of codec internal audio paths and active - * DACs/ADCs. - * o Platform power domain - can support external components i.e. amps and - * mic/headphone insertion events. - * o Automatic Mic Bias support - * o Jack insertion power event initiation - e.g. hp insertion will enable - * sinks, dacs, etc - * o Delayed power down of audio subsystem to reduce pops between a quick - * device reopen. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-dapm.c -- ALSA SoC Dynamic Audio Power Management +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// +// Features: +// o Changes power status of internal codec blocks depending on the +// dynamic configuration of codec internal audio paths and active +// DACs/ADCs. +// o Platform power domain - can support external components i.e. amps and +// mic/headphone insertion events. +// o Automatic Mic Bias support +// o Jack insertion power event initiation - e.g. hp insertion will enable +// sinks, dacs, etc +// o Delayed power down of audio subsystem to reduce pops between a quick +// device reopen. #include <linux/module.h> #include <linux/moduleparam.h> @@ -3662,7 +3656,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, struct snd_pcm_substream substream; struct snd_pcm_hw_params *params = NULL; struct snd_pcm_runtime *runtime = NULL; - u64 fmt; + unsigned int fmt; int ret; if (WARN_ON(!config) || @@ -4073,6 +4067,13 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) continue; } + /* let users know there is no DAI to link */ + if (!dai_w->priv) { + dev_dbg(card->dev, "dai widget %s has no DAI\n", + dai_w->name); + continue; + } + dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index 7ac745df1412..a9ea172a66a7 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -1,13 +1,8 @@ -/* - * soc-devres.c -- ALSA SoC Audio Layer devres functions - * - * Copyright (C) 2013 Linaro Ltd - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-devres.c -- ALSA SoC Audio Layer devres functions +// +// Copyright (C) 2013 Linaro Ltd #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 56a541b9ff9e..52fd7af952a5 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -1,17 +1,8 @@ -/* - * Copyright (C) 2013, Analog Devices Inc. - * Author: Lars-Peter Clausen <lars@metafoo.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2013, Analog Devices Inc. +// Author: Lars-Peter Clausen <lars@metafoo.de> + #include <linux/module.h> #include <linux/init.h> #include <linux/dmaengine.h> @@ -197,7 +188,7 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea case 32: case 64: if (addr_widths & (1 << (bits / 8))) - hw.formats |= (1LL << i); + hw.formats |= pcm_format_to_bits(i); break; default: /* Unsupported types */ @@ -343,7 +334,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( static int dmaengine_copy_user(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + void __user *buf, unsigned long bytes) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component = @@ -359,18 +350,17 @@ static int dmaengine_copy_user(struct snd_pcm_substream *substream, int ret; if (is_playback) - if (copy_from_user(dma_ptr, (void __user *)buf, bytes)) + if (copy_from_user(dma_ptr, buf, bytes)) return -EFAULT; if (process) { - ret = process(substream, channel, hwoff, - (void __user *)buf, bytes); + ret = process(substream, channel, hwoff, (__force void *)buf, bytes); if (ret < 0) return ret; } if (!is_playback) - if (copy_to_user((void __user *)buf, dma_ptr, bytes)) + if (copy_to_user(buf, dma_ptr, bytes)) return -EFAULT; return 0; diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 026cd5347e53..1ff9175e9d5e 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -1,15 +1,10 @@ -/* - * soc-io.c -- ASoC register I/O helpers - * - * Copyright 2009-2011 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-io.c -- ASoC register I/O helpers +// +// Copyright 2009-2011 Wolfson Microelectronics PLC. +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> #include <linux/i2c.h> #include <linux/spi/spi.h> diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index b2b16044ae80..c7b990abdbaa 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -1,15 +1,10 @@ -/* - * soc-jack.c -- ALSA SoC jack handling - * - * Copyright 2008 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-jack.c -- ALSA SoC jack handling +// +// Copyright 2008 Wolfson Microelectronics PLC. +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> #include <sound/jack.h> #include <sound/soc.h> diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 7144a51ddfa9..592efb370c44 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -1,20 +1,15 @@ -/* - * soc-ops.c -- Generic ASoC operations - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - * with code, comments and ideas from :- - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-ops.c -- Generic ASoC operations +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> +// with code, comments and ideas from :- +// Richard Purdie <richard@openedhand.com> #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 5e7ae47a9658..e8b98bfd4cf1 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1,20 +1,14 @@ -/* - * soc-pcm.c -- ALSA SoC PCM - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * Copyright (C) 2010 Slimlogic Ltd. - * Copyright (C) 2010 Texas Instruments Inc. - * - * Authors: Liam Girdwood <lrg@ti.com> - * Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-pcm.c -- ALSA SoC PCM +// +// Copyright 2005 Wolfson Microelectronics PLC. +// Copyright 2005 Openedhand Ltd. +// Copyright (C) 2010 Slimlogic Ltd. +// Copyright (C) 2010 Texas Instruments Inc. +// +// Authors: Liam Girdwood <lrg@ti.com> +// Mark Brown <broonie@opensource.wolfsonmicro.com> #include <linux/kernel.h> #include <linux/init.h> @@ -448,6 +442,29 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) hw->rate_max = min_not_zero(hw->rate_max, rate_max); } +static int soc_pcm_components_close(struct snd_pcm_substream *substream, + struct snd_soc_component *last) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (component == last) + break; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + + return 0; +} + /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls @@ -462,7 +479,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; - int i, ret = 0, __ret; + int i, ret = 0; pinctrl_pm_select_default_state(cpu_dai->dev); for (i = 0; i < rtd->num_codecs; i++) @@ -486,7 +503,6 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - ret = 0; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -494,16 +510,15 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) !component->driver->ops->open) continue; - __ret = component->driver->ops->open(substream); - if (__ret < 0) { + ret = component->driver->ops->open(substream); + if (ret < 0) { dev_err(component->dev, "ASoC: can't open component %s: %d\n", - component->name, __ret); - ret = __ret; + component->name, ret); + goto component_err; } } - if (ret < 0) - goto component_err; + component = NULL; for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; @@ -612,15 +627,7 @@ codec_dai_err: } component_err: - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->close) - continue; - - component->driver->ops->close(substream); - } + soc_pcm_components_close(substream, component); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -714,15 +721,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->close) - continue; - - component->driver->ops->close(substream); - } + soc_pcm_components_close(substream, NULL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { @@ -860,8 +859,20 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; + /* perform any topology hw_params fixups before DAI */ + if (rtd->dai_link->be_hw_params_fixup) { + ret = rtd->dai_link->be_hw_params_fixup(rtd, params); + if (ret < 0) { + dev_err(rtd->dev, + "ASoC: hw_params topology fixup failed %d\n", + ret); + return ret; + } + } + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { @@ -874,6 +885,29 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, return 0; } +static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_component *last) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (component == last) + break; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + + return 0; +} + /* * Called by ALSA when the hardware params are set by application. This * function can also be called multiple times and can allocate buffers @@ -886,7 +920,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret = 0, __ret; + int i, ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); if (rtd->dai_link->ops->hw_params) { @@ -944,7 +978,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - ret = 0; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -952,16 +985,15 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, !component->driver->ops->hw_params) continue; - __ret = component->driver->ops->hw_params(substream, params); - if (__ret < 0) { + ret = component->driver->ops->hw_params(substream, params); + if (ret < 0) { dev_err(component->dev, "ASoC: %s hw params failed: %d\n", - component->name, __ret); - ret = __ret; + component->name, ret); + goto component_err; } } - if (ret < 0) - goto component_err; + component = NULL; /* store the parameters for each DAIs */ cpu_dai->rate = params_rate(params); @@ -977,15 +1009,7 @@ out: return ret; component_err: - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->hw_free) - continue; - - component->driver->ops->hw_free(substream); - } + soc_pcm_components_hw_free(substream, component); if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); @@ -1014,8 +1038,6 @@ codec_err: static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -1052,15 +1074,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) rtd->dai_link->ops->hw_free(substream); /* free any component resources */ - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (!component->driver->ops || - !component->driver->ops->hw_free) - continue; - - component->driver->ops->hw_free(substream); - } + soc_pcm_components_hw_free(substream, NULL); /* now free hw params for the DAIs */ for (i = 0; i < rtd->num_codecs; i++) { @@ -1165,6 +1179,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; + /* clearing the previous total delay */ + runtime->delay = 0; + for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; @@ -1176,6 +1193,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) offset = component->driver->ops->pointer(substream); break; } + /* base delay if assigned in pointer callback */ + delay = runtime->delay; if (cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); @@ -1658,29 +1677,28 @@ unwind: } static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, - struct snd_soc_pcm_stream *stream, - u64 formats) + struct snd_soc_pcm_stream *stream) { runtime->hw.rate_min = stream->rate_min; runtime->hw.rate_max = stream->rate_max; runtime->hw.channels_min = stream->channels_min; runtime->hw.channels_max = stream->channels_max; if (runtime->hw.formats) - runtime->hw.formats &= formats & stream->formats; + runtime->hw.formats &= stream->formats; else - runtime->hw.formats = formats & stream->formats; + runtime->hw.formats = stream->formats; runtime->hw.rates = stream->rates; } -static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream) +static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, + u64 *formats) { struct snd_soc_pcm_runtime *fe = substream->private_data; struct snd_soc_dpcm *dpcm; - u64 formats = ULLONG_MAX; int stream = substream->stream; if (!fe->dai_link->dpcm_merged_format) - return formats; + return; /* * It returns merged BE codec format @@ -1694,17 +1712,132 @@ static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream) int i; for (i = 0; i < be->num_codecs; i++) { + /* + * Skip CODECs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(be->codec_dais[i], + stream)) + continue; + codec_dai_drv = be->codec_dais[i]->driver; if (stream == SNDRV_PCM_STREAM_PLAYBACK) codec_stream = &codec_dai_drv->playback; else codec_stream = &codec_dai_drv->capture; - formats &= codec_stream->formats; + *formats &= codec_stream->formats; } } +} + +static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, + unsigned int *channels_min, + unsigned int *channels_max) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dpcm *dpcm; + int stream = substream->stream; - return formats; + if (!fe->dai_link->dpcm_merged_chan) + return; + + /* + * It returns merged BE codec channel; + * if FE want to use it (= dpcm_merged_chan) + */ + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; + struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_pcm_stream *codec_stream; + struct snd_soc_pcm_stream *cpu_stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; + + *channels_min = max(*channels_min, cpu_stream->channels_min); + *channels_max = min(*channels_max, cpu_stream->channels_max); + + /* + * chan min/max cannot be enforced if there are multiple CODEC + * DAIs connected to a single CPU DAI, use CPU DAI's directly + */ + if (be->num_codecs == 1) { + codec_dai_drv = be->codec_dais[0]->driver; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &codec_dai_drv->playback; + else + codec_stream = &codec_dai_drv->capture; + + *channels_min = max(*channels_min, + codec_stream->channels_min); + *channels_max = min(*channels_max, + codec_stream->channels_max); + } + } +} + +static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, + unsigned int *rates, + unsigned int *rate_min, + unsigned int *rate_max) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dpcm *dpcm; + int stream = substream->stream; + + if (!fe->dai_link->dpcm_merged_rate) + return; + + /* + * It returns merged BE codec channel; + * if FE want to use it (= dpcm_merged_chan) + */ + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; + struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_pcm_stream *codec_stream; + struct snd_soc_pcm_stream *cpu_stream; + int i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_stream = &cpu_dai_drv->playback; + else + cpu_stream = &cpu_dai_drv->capture; + + *rate_min = max(*rate_min, cpu_stream->rate_min); + *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max); + *rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates); + + for (i = 0; i < be->num_codecs; i++) { + /* + * Skip CODECs which don't support the current stream + * type. See soc_pcm_init_runtime_hw() for more details + */ + if (!snd_soc_dai_stream_valid(be->codec_dais[i], + stream)) + continue; + + codec_dai_drv = be->codec_dais[i]->driver; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &codec_dai_drv->playback; + else + codec_stream = &codec_dai_drv->capture; + + *rate_min = max(*rate_min, codec_stream->rate_min); + *rate_max = min_not_zero(*rate_max, + codec_stream->rate_max); + *rates = snd_pcm_rate_mask_intersect(*rates, + codec_stream->rates); + } + } } static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) @@ -1713,12 +1846,17 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; - u64 format = dpcm_runtime_base_format(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + + dpcm_runtime_merge_format(substream, &runtime->hw.formats); + dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min, + &runtime->hw.channels_max); + dpcm_runtime_merge_rate(substream, &runtime->hw.rates, + &runtime->hw.rate_min, &runtime->hw.rate_max); } static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd); @@ -2543,106 +2681,113 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) return ret; } -/* Called by DAPM mixer/mux changes to update audio routing between PCMs and - * any DAI links. - */ -int soc_dpcm_runtime_update(struct snd_soc_card *card) +static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) { - struct snd_soc_pcm_runtime *fe; - int old, new, paths; + struct snd_soc_dapm_widget_list *list; + int count, paths; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - list_for_each_entry(fe, &card->rtd_list, list) { - struct snd_soc_dapm_widget_list *list; + if (!fe->dai_link->dynamic) + return 0; - /* make sure link is FE */ - if (!fe->dai_link->dynamic) - continue; + /* only check active links */ + if (!fe->cpu_dai->active) + return 0; - /* only check active links */ - if (!fe->cpu_dai->active) - continue; + /* DAPM sync will call this to update DSP paths */ + dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n", + new ? "new" : "old", fe->dai_link->name); - /* DAPM sync will call this to update DSP paths */ - dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n", - fe->dai_link->name); + /* skip if FE doesn't have playback capability */ + if (!fe->cpu_dai->driver->playback.channels_min || + !fe->codec_dai->driver->playback.channels_min) + goto capture; - /* skip if FE doesn't have playback capability */ - if (!fe->cpu_dai->driver->playback.channels_min - || !fe->codec_dai->driver->playback.channels_min) - goto capture; - - /* skip if FE isn't currently playing */ - if (!fe->cpu_dai->playback_active - || !fe->codec_dai->playback_active) - goto capture; - - paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); - if (paths < 0) { - dev_warn(fe->dev, "ASoC: %s no valid %s path\n", - fe->dai_link->name, "playback"); - mutex_unlock(&card->mutex); - return paths; - } + /* skip if FE isn't currently playing */ + if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active) + goto capture; - /* update any new playback paths */ - new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1); - if (new) { - dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); - } + paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); + if (paths < 0) { + dev_warn(fe->dev, "ASoC: %s no valid %s path\n", + fe->dai_link->name, "playback"); + return paths; + } - /* update any old playback paths */ - old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0); - if (old) { + /* update any playback paths */ + count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new); + if (count) { + if (new) + dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK); + else dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); - } - dpcm_path_put(&list); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK); + } + + dpcm_path_put(&list); + capture: - /* skip if FE doesn't have capture capability */ - if (!fe->cpu_dai->driver->capture.channels_min - || !fe->codec_dai->driver->capture.channels_min) - continue; + /* skip if FE doesn't have capture capability */ + if (!fe->cpu_dai->driver->capture.channels_min || + !fe->codec_dai->driver->capture.channels_min) + return 0; - /* skip if FE isn't currently capturing */ - if (!fe->cpu_dai->capture_active - || !fe->codec_dai->capture_active) - continue; + /* skip if FE isn't currently capturing */ + if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active) + return 0; - paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); - if (paths < 0) { - dev_warn(fe->dev, "ASoC: %s no valid %s path\n", - fe->dai_link->name, "capture"); - mutex_unlock(&card->mutex); - return paths; - } + paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); + if (paths < 0) { + dev_warn(fe->dev, "ASoC: %s no valid %s path\n", + fe->dai_link->name, "capture"); + return paths; + } - /* update any new capture paths */ - new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1); - if (new) { + /* update any old capture paths */ + count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new); + if (count) { + if (new) dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); - } - - /* update any old capture paths */ - old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0); - if (old) { + else dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); - dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); - } - dpcm_path_put(&list); + dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE); + dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE); } - mutex_unlock(&card->mutex); + dpcm_path_put(&list); + return 0; } + +/* Called by DAPM mixer/mux changes to update audio routing between PCMs and + * any DAI links. + */ +int soc_dpcm_runtime_update(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *fe; + int ret = 0; + + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + /* shutdown all old paths first */ + list_for_each_entry(fe, &card->rtd_list, list) { + ret = soc_dpcm_fe_runtime_update(fe, 0); + if (ret) + goto out; + } + + /* bring new paths up */ + list_for_each_entry(fe, &card->rtd_list, list) { + ret = soc_dpcm_fe_runtime_update(fe, 1); + if (ret) + goto out; + } + +out: + mutex_unlock(&card->mutex); + return ret; +} int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) { struct snd_soc_dpcm *dpcm; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 53f121a50c97..66e77e020745 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1,29 +1,24 @@ -/* - * soc-topology.c -- ALSA SoC Topology - * - * Copyright (C) 2012 Texas Instruments Inc. - * Copyright (C) 2015 Intel Corporation. - * - * Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> - * K, Mythri P <mythri.p.k@intel.com> - * Prusty, Subhransu S <subhransu.s.prusty@intel.com> - * B, Jayachandran <jayachandran.b@intel.com> - * Abdullah, Omair M <omair.m.abdullah@intel.com> - * Jin, Yao <yao.jin@intel.com> - * Lin, Mengdong <mengdong.lin@intel.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * Add support to read audio firmware topology alongside firmware text. The - * topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, - * equalizers, firmware, coefficients etc. - * - * This file only manages the core ALSA and ASoC components, all other bespoke - * firmware topology data is passed to component drivers for bespoke handling. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-topology.c -- ALSA SoC Topology +// +// Copyright (C) 2012 Texas Instruments Inc. +// Copyright (C) 2015 Intel Corporation. +// +// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> +// K, Mythri P <mythri.p.k@intel.com> +// Prusty, Subhransu S <subhransu.s.prusty@intel.com> +// B, Jayachandran <jayachandran.b@intel.com> +// Abdullah, Omair M <omair.m.abdullah@intel.com> +// Jin, Yao <yao.jin@intel.com> +// Lin, Mengdong <mengdong.lin@intel.com> +// +// Add support to read audio firmware topology alongside firmware text. The +// topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, +// equalizers, firmware, coefficients etc. +// +// This file only manages the core ALSA and ASoC components, all other bespoke +// firmware topology data is passed to component drivers for bespoke handling. #include <linux/kernel.h> #include <linux/export.h> @@ -259,7 +254,7 @@ static int soc_tplg_vendor_load_(struct soc_tplg *tplg, int ret = 0; if (tplg->comp && tplg->ops && tplg->ops->vendor_load) - ret = tplg->ops->vendor_load(tplg->comp, hdr); + ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); else { dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", hdr->vendor_type); @@ -291,7 +286,8 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_load) - return tplg->ops->widget_load(tplg->comp, w, tplg_w); + return tplg->ops->widget_load(tplg->comp, tplg->index, w, + tplg_w); return 0; } @@ -302,27 +298,30 @@ static int soc_tplg_widget_ready(struct soc_tplg *tplg, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { if (tplg->comp && tplg->ops && tplg->ops->widget_ready) - return tplg->ops->widget_ready(tplg->comp, w, tplg_w); + return tplg->ops->widget_ready(tplg->comp, tplg->index, w, + tplg_w); return 0; } /* pass DAI configurations to component driver for extra initialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, - struct snd_soc_dai_driver *dai_drv) + struct snd_soc_dai_driver *dai_drv, + struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { if (tplg->comp && tplg->ops && tplg->ops->dai_load) - return tplg->ops->dai_load(tplg->comp, dai_drv); + return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, + pcm, dai); return 0; } /* pass link configurations to component driver for extra initialization */ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, - struct snd_soc_dai_link *link) + struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { if (tplg->comp && tplg->ops && tplg->ops->link_load) - return tplg->ops->link_load(tplg->comp, link); + return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); return 0; } @@ -643,7 +642,8 @@ static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { if (tplg->comp && tplg->ops && tplg->ops->control_load) - return tplg->ops->control_load(tplg->comp, k, hdr); + return tplg->ops->control_load(tplg->comp, tplg->index, k, + hdr); return 0; } @@ -1100,6 +1100,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, return 0; } +/* optionally pass new dynamic kcontrol to component driver. */ +static int soc_tplg_add_route(struct soc_tplg *tplg, + struct snd_soc_dapm_route *route) +{ + if (tplg->comp && tplg->ops && tplg->ops->dapm_route_load) + return tplg->ops->dapm_route_load(tplg->comp, tplg->index, + route); + + return 0; +} + static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { @@ -1148,6 +1159,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, else route.control = elem->control; + soc_tplg_add_route(tplg, &route); + /* add route, but keep going if some fail */ snd_soc_dapm_add_routes(dapm, &route, 1); } @@ -1702,7 +1715,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, dai_drv->compress_new = snd_soc_new_compress; /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); kfree(dai_drv); @@ -1772,7 +1785,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, set_link_flags(link, pcm->flag_mask, pcm->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); kfree(link); @@ -2080,7 +2093,7 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, set_link_flags(link, cfg->flag_mask, cfg->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_link_load(tplg, link); + ret = soc_tplg_dai_link_load(tplg, link, cfg); if (ret < 0) { dev_err(tplg->dev, "ASoC: physical link loading failed\n"); return ret; @@ -2202,7 +2215,7 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg, set_dai_flags(dai_drv, d->flag_mask, d->flags); /* pass control to component driver for optional further init */ - ret = soc_tplg_dai_load(tplg, dai_drv); + ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); return ret; @@ -2311,7 +2324,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, _manifest); + return tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ kfree(_manifest); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 2d9e98bd1530..e0c93496c0cd 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -1,17 +1,11 @@ -/* - * soc-util.c -- ALSA SoC Audio Layer utility functions - * - * Copyright 2009 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * Liam Girdwood <lrg@slimlogic.co.uk> - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// soc-util.c -- ALSA SoC Audio Layer utility functions +// +// Copyright 2009 Wolfson Microelectronics PLC. +// +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> +// Liam Girdwood <lrg@slimlogic.co.uk> #include <linux/platform_device.h> #include <linux/export.h> @@ -381,6 +375,6 @@ int __init snd_soc_util_init(void) void __exit snd_soc_util_exit(void) { - platform_device_unregister(soc_dummy_dev); platform_driver_unregister(&soc_dummy_driver); + platform_device_unregister(soc_dummy_dev); } diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index d8b6936e544e..313dab2857ef 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -91,7 +91,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); /* Stop the player */ - snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(player->substream); } ret = IRQ_HANDLED; @@ -105,7 +105,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); /* Stop the player */ - snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(player->substream); ret = IRQ_HANDLED; } @@ -138,7 +138,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) dev_err(player->dev, "Underflow recovery failed\n"); /* Stop the player */ - snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(player->substream); ret = IRQ_HANDLED; } diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index ee0055e60852..7b63d35ef428 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -65,7 +65,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { dev_err(reader->dev, "FIFO error detected\n"); - snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stop_xrun(reader->substream); ret = IRQ_HANDLED; } diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 48f9ddd94016..9b2681397dba 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -6,6 +6,7 @@ config SND_SOC_STM32_SAI depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO + select SND_PCM_IEC958 help Say Y if you want to enable SAI for STM32 diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index db73fef3e500..706ff005234f 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -149,7 +149,7 @@ static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) unsigned int old_pos = priv->pos; unsigned int cur_size = size; - dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %zu\n", + dev_dbg(rtd->dev, "%s: buff_add :%pK, pos = %d, size = %zu\n", __func__, &pcm_buff[priv->pos], priv->pos, size); if ((priv->pos + size) > buff_size) { @@ -269,16 +269,10 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd) static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; - struct snd_soc_pcm_runtime *rtd; - struct stm32_adfsdm_priv *priv; substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - if (substream) { - rtd = substream->private_data; - priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); - + if (substream) snd_pcm_lib_preallocate_free_for_all(pcm); - } } static struct snd_soc_component_driver stm32_adfsdm_soc_platform = { diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index cfeb219e1d78..06fba9650ac4 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -96,7 +96,8 @@ * @slot_mask: rx or tx active slots mask. set at init or at runtime * @data_size: PCM data width. corresponds to PCM substream width. * @spdif_frm_cnt: S/PDIF playback frame counter - * @spdif_status_bits: S/PDIF status bits + * @snd_aes_iec958: iec958 data + * @ctrl_lock: control lock */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -125,7 +126,8 @@ struct stm32_sai_sub_data { int slot_mask; int data_size; unsigned int spdif_frm_cnt; - unsigned char spdif_status_bits[SAI_IEC60958_STATUS_BYTES]; + struct snd_aes_iec958 iec958; + struct mutex ctrl_lock; /* protect resources accessed by controls */ }; enum stm32_sai_fifo_th { @@ -184,10 +186,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) } } -static const unsigned char default_status_bits[SAI_IEC60958_STATUS_BYTES] = { - 0, 0, 0, IEC958_AES3_CON_FS_48000, -}; - static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .reg_bits = 32, .reg_stride = 4, @@ -210,6 +208,49 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { .fast_io = true, }; +static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int snd_pcm_iec958_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uctl) +{ + struct stm32_sai_sub_data *sai = snd_kcontrol_chip(kcontrol); + + mutex_lock(&sai->ctrl_lock); + memcpy(uctl->value.iec958.status, sai->iec958.status, 4); + mutex_unlock(&sai->ctrl_lock); + + return 0; +} + +static int snd_pcm_iec958_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uctl) +{ + struct stm32_sai_sub_data *sai = snd_kcontrol_chip(kcontrol); + + mutex_lock(&sai->ctrl_lock); + memcpy(sai->iec958.status, uctl->value.iec958.status, 4); + mutex_unlock(&sai->ctrl_lock); + + return 0; +} + +static const struct snd_kcontrol_new iec958_ctls = { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_pcm_iec958_info, + .get = snd_pcm_iec958_get, + .put = snd_pcm_iec958_put, +}; + static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; @@ -259,11 +300,8 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) status = SNDRV_PCM_STATE_XRUN; } - if (status != SNDRV_PCM_STATE_RUNNING) { - snd_pcm_stream_lock(sai->substream); - snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(sai->substream); - } + if (status != SNDRV_PCM_STATE_RUNNING) + snd_pcm_stop_xrun(sai->substream); return IRQ_HANDLED; } @@ -619,6 +657,59 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) } } +static void stm32_sai_init_iec958_status(struct stm32_sai_sub_data *sai) +{ + unsigned char *cs = sai->iec958.status; + + cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE; + cs[1] = IEC958_AES1_CON_GENERAL; + cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC; + cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID; +} + +static void stm32_sai_set_iec958_status(struct stm32_sai_sub_data *sai, + struct snd_pcm_runtime *runtime) +{ + if (!runtime) + return; + + /* Force the sample rate according to runtime rate */ + mutex_lock(&sai->ctrl_lock); + switch (runtime->rate) { + case 22050: + sai->iec958.status[3] = IEC958_AES3_CON_FS_22050; + break; + case 44100: + sai->iec958.status[3] = IEC958_AES3_CON_FS_44100; + break; + case 88200: + sai->iec958.status[3] = IEC958_AES3_CON_FS_88200; + break; + case 176400: + sai->iec958.status[3] = IEC958_AES3_CON_FS_176400; + break; + case 24000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_24000; + break; + case 48000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_48000; + break; + case 96000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_96000; + break; + case 192000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_192000; + break; + case 32000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_32000; + break; + default: + sai->iec958.status[3] = IEC958_AES3_CON_FS_NOTID; + break; + } + mutex_unlock(&sai->ctrl_lock); +} + static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) { @@ -709,7 +800,11 @@ static int stm32_sai_hw_params(struct snd_pcm_substream *substream, sai->data_size = params_width(params); - if (!STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + /* Rate not already set in runtime structure */ + substream->runtime->rate = params_rate(params); + stm32_sai_set_iec958_status(sai, substream->runtime); + } else { ret = stm32_sai_set_slots(cpu_dai); if (ret < 0) return ret; @@ -789,6 +884,20 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, sai->substream = NULL; } +static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__); + return snd_ctl_add(rtd->pcm->card, + snd_ctl_new1(&iec958_ctls, sai)); + } + + return 0; +} + static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); @@ -809,6 +918,10 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) else snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); + /* Next settings are not relevant for spdif mode */ + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + return 0; + cr1_mask = SAI_XCR1_RX_TX; if (STM_SAI_IS_CAPTURE(sai)) cr1 |= SAI_XCR1_RX_TX; @@ -820,10 +933,6 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) sai->synco, sai->synci); } - if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) - memcpy(sai->spdif_status_bits, default_status_bits, - sizeof(default_status_bits)); - cr1_mask |= SAI_XCR1_SYNCEN_MASK; cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); @@ -861,7 +970,7 @@ static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, /* Set channel status bit */ byte = frm_cnt >> 3; mask = 1 << (frm_cnt - (byte << 3)); - if (sai->spdif_status_bits[byte] & mask) + if (sai->iec958.status[byte] & mask) *ptr |= 0x04000000; ptr++; @@ -888,6 +997,7 @@ static const struct snd_pcm_hardware stm32_sai_pcm_hw = { static struct snd_soc_dai_driver stm32_sai_playback_dai[] = { { .probe = stm32_sai_dai_probe, + .pcm_new = stm32_sai_pcm_new, .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, @@ -998,6 +1108,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n"); return -EINVAL; } + stm32_sai_init_iec958_status(sai); sai->spdif = true; sai->master = true; } @@ -1114,6 +1225,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) sai->id = (uintptr_t)of_id->data; sai->pdev = pdev; + mutex_init(&sai->ctrl_lock); platform_set_drvdata(pdev, sai); sai->pdata = dev_get_drvdata(pdev->dev.parent); diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index affad46bf188..682ef33afb5f 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -377,7 +377,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) ret = clk_prepare_enable(ac97->clk_ac97); if (ret) { dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); - goto err; + goto err_clk_put; } ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops); diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h index 774fc6ad2026..2e561e946de2 100644 --- a/sound/soc/tegra/tegra30_i2s.h +++ b/sound/soc/tegra/tegra30_i2s.h @@ -173,7 +173,7 @@ /* Number of slots in frame, minus 1 */ #define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 16 #define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7 -#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT) +#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT) /* TDM mode slot enable bitmask */ #define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8 diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 5197d6b18cb6..98d87801d57a 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -190,14 +190,14 @@ static int tegra_alc5632_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; - goto err; + goto err_put_codec_of_node; } tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node; ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); if (ret) - goto err; + goto err_put_cpu_of_node; ret = snd_soc_register_card(card); if (ret) { @@ -210,6 +210,13 @@ static int tegra_alc5632_probe(struct platform_device *pdev) err_fini_utils: tegra_asoc_utils_fini(&alc5632->util_data); +err_put_cpu_of_node: + of_node_put(tegra_alc5632_dai.cpu_of_node); + tegra_alc5632_dai.cpu_of_node = NULL; + tegra_alc5632_dai.platform_of_node = NULL; +err_put_codec_of_node: + of_node_put(tegra_alc5632_dai.codec_of_node); + tegra_alc5632_dai.codec_of_node = NULL; err: return ret; } @@ -223,6 +230,12 @@ static int tegra_alc5632_remove(struct platform_device *pdev) tegra_asoc_utils_fini(&machine->util_data); + of_node_put(tegra_alc5632_dai.cpu_of_node); + tegra_alc5632_dai.cpu_of_node = NULL; + tegra_alc5632_dai.platform_of_node = NULL; + of_node_put(tegra_alc5632_dai.codec_of_node); + tegra_alc5632_dai.codec_of_node = NULL; + return 0; } diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index 0e4805c7b4ca..7081f15302cc 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c @@ -264,13 +264,13 @@ static int tegra_rt5677_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; - goto err; + goto err_put_codec_of_node; } tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node; ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); if (ret) - goto err; + goto err_put_cpu_of_node; ret = snd_soc_register_card(card); if (ret) { @@ -283,6 +283,13 @@ static int tegra_rt5677_probe(struct platform_device *pdev) err_fini_utils: tegra_asoc_utils_fini(&machine->util_data); +err_put_cpu_of_node: + of_node_put(tegra_rt5677_dai.cpu_of_node); + tegra_rt5677_dai.cpu_of_node = NULL; + tegra_rt5677_dai.platform_of_node = NULL; +err_put_codec_of_node: + of_node_put(tegra_rt5677_dai.codec_of_node); + tegra_rt5677_dai.codec_of_node = NULL; err: return ret; } @@ -296,6 +303,12 @@ static int tegra_rt5677_remove(struct platform_device *pdev) tegra_asoc_utils_fini(&machine->util_data); + tegra_rt5677_dai.platform_of_node = NULL; + of_node_put(tegra_rt5677_dai.codec_of_node); + tegra_rt5677_dai.codec_of_node = NULL; + of_node_put(tegra_rt5677_dai.cpu_of_node); + tegra_rt5677_dai.cpu_of_node = NULL; + return 0; } diff --git a/sound/soc/uniphier/aio-core.c b/sound/soc/uniphier/aio-core.c index 638cb3fc5f7b..9bcba06ba52e 100644 --- a/sound/soc/uniphier/aio-core.c +++ b/sound/soc/uniphier/aio-core.c @@ -265,6 +265,57 @@ void aio_port_reset(struct uniphier_aio_sub *sub) } /** + * aio_port_set_ch - set channels of LPCM + * @sub: the AIO substream pointer, PCM substream only + * @ch : count of channels + * + * Set suitable slot selecting to input/output port block of AIO. + * + * This function may return error if non-PCM substream. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +static int aio_port_set_ch(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 slotsel_2ch[] = { + 0, 0, 0, 0, 0, + }; + u32 slotsel_multi[] = { + OPORTMXTYSLOTCTR_SLOTSEL_SLOT0, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT1, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT2, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT3, + OPORTMXTYSLOTCTR_SLOTSEL_SLOT4, + }; + u32 mode, *slotsel; + int i; + + switch (params_channels(&sub->params)) { + case 8: + case 6: + mode = OPORTMXTYSLOTCTR_MODE; + slotsel = slotsel_multi; + break; + case 2: + mode = 0; + slotsel = slotsel_2ch; + break; + default: + return -EINVAL; + } + + for (i = 0; i < AUD_MAX_SLOTSEL; i++) { + regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), + OPORTMXTYSLOTCTR_MODE, mode); + regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), + OPORTMXTYSLOTCTR_SLOTSEL_MASK, slotsel[i]); + } + + return 0; +} + +/** * aio_port_set_rate - set sampling rate of LPCM * @sub: the AIO substream pointer, PCM substream only * @rate: Sampling rate in Hz. @@ -276,7 +327,7 @@ void aio_port_reset(struct uniphier_aio_sub *sub) * * Return: Zero if successful, otherwise a negative value on error. */ -int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) +static int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) { struct regmap *r = sub->aio->chip->regmap; struct device *dev = &sub->aio->chip->pdev->dev; @@ -395,7 +446,7 @@ int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) * * Return: Zero if successful, otherwise a negative value on error. */ -int aio_port_set_fmt(struct uniphier_aio_sub *sub) +static int aio_port_set_fmt(struct uniphier_aio_sub *sub) { struct regmap *r = sub->aio->chip->regmap; struct device *dev = &sub->aio->chip->pdev->dev; @@ -460,7 +511,7 @@ int aio_port_set_fmt(struct uniphier_aio_sub *sub) * * Return: Zero if successful, otherwise a negative value on error. */ -int aio_port_set_clk(struct uniphier_aio_sub *sub) +static int aio_port_set_clk(struct uniphier_aio_sub *sub) { struct uniphier_aio_chip *chip = sub->aio->chip; struct device *dev = &sub->aio->chip->pdev->dev; @@ -575,6 +626,10 @@ int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, rate = params_rate(params); } + ret = aio_port_set_ch(sub); + if (ret) + return ret; + ret = aio_port_set_rate(sub, rate); if (ret) return ret; @@ -731,15 +786,28 @@ void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol) int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through) { struct regmap *r = sub->aio->chip->regmap; - u32 v; + u32 memfmt, v; if (sub->swm->dir == PORT_DIR_OUTPUT) { - if (pass_through) + if (pass_through) { v = PBOUTMXCTR0_ENDIAN_0123 | PBOUTMXCTR0_MEMFMT_STREAM; - else - v = PBOUTMXCTR0_ENDIAN_3210 | - PBOUTMXCTR0_MEMFMT_2CH; + } else { + switch (params_channels(&sub->params)) { + case 2: + memfmt = PBOUTMXCTR0_MEMFMT_2CH; + break; + case 6: + memfmt = PBOUTMXCTR0_MEMFMT_6CH; + break; + case 8: + memfmt = PBOUTMXCTR0_MEMFMT_8CH; + break; + default: + return -EINVAL; + } + v = PBOUTMXCTR0_ENDIAN_3210 | memfmt; + } regmap_write(r, PBOUTMXCTR0(sub->swm->oif.map), v); regmap_write(r, PBOUTMXCTR1(sub->swm->oif.map), 0); diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 2d9b7dde2ffa..ee90e6c3937c 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -219,15 +219,10 @@ static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id, unsigned int freq_out) { struct uniphier_aio *aio = uniphier_priv(dai); - struct device *dev = &aio->chip->pdev->dev; int ret; if (!is_valid_pll(aio->chip, pll_id)) return -EINVAL; - if (!aio->chip->plls[pll_id].enable) { - dev_err(dev, "PLL(%d) is not implemented\n", pll_id); - return -ENOTSUPP; - } ret = aio_chip_set_pll(aio->chip, pll_id, freq_out); if (ret < 0) diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c index ab04d3331be9..de962df245ba 100644 --- a/sound/soc/uniphier/aio-ld11.c +++ b/sound/soc/uniphier/aio-ld11.c @@ -286,7 +286,7 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .formats = SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_48000, .channels_min = 2, - .channels_max = 2, + .channels_max = 8, }, .ops = &uniphier_aio_i2s_ops, }, diff --git a/sound/soc/uniphier/aio-reg.h b/sound/soc/uniphier/aio-reg.h index 45fdc6ae358a..734395dbcffb 100644 --- a/sound/soc/uniphier/aio-reg.h +++ b/sound/soc/uniphier/aio-reg.h @@ -374,6 +374,7 @@ #define OPORTMXTYVOLGAINSTATUS(n, m) (0x42108 + 0x400 * (n) + 0x20 * (m)) #define OPORTMXTYVOLGAINSTATUS_CUR_MASK GENMASK(15, 0) #define OPORTMXTYSLOTCTR(n, m) (0x42114 + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXTYSLOTCTR_MODE BIT(15) #define OPORTMXTYSLOTCTR_SLOTSEL_MASK GENMASK(11, 8) #define OPORTMXTYSLOTCTR_SLOTSEL_SLOT0 (0x8 << 8) #define OPORTMXTYSLOTCTR_SLOTSEL_SLOT1 (0x9 << 8) diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index aa89c2f6fa24..ca6ccbae0ee8 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -141,6 +141,9 @@ enum IEC61937_PC { #define AUD_MIN_FRAGMENT_SIZE (4 * 1024) #define AUD_MAX_FRAGMENT_SIZE (16 * 1024) +/* max 5 slots, 10 channels, 2 channel in 1 slot */ +#define AUD_MAX_SLOTSEL 5 + /* * This is a selector for virtual register map of AIO. * @@ -322,9 +325,6 @@ int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id, void aio_chip_init(struct uniphier_aio_chip *chip); int aio_init(struct uniphier_aio_sub *sub); void aio_port_reset(struct uniphier_aio_sub *sub); -int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate); -int aio_port_set_fmt(struct uniphier_aio_sub *sub); -int aio_port_set_clk(struct uniphier_aio_sub *sub); int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, const struct snd_pcm_hw_params *params); void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable); diff --git a/sound/soc/zte/zx-tdm.c b/sound/soc/zte/zx-tdm.c index dc955272f58b..389272eeba9a 100644 --- a/sound/soc/zte/zx-tdm.c +++ b/sound/soc/zte/zx-tdm.c @@ -144,8 +144,8 @@ static void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on) #define ZX_TDM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) #define ZX_TDM_FMTBIT \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_MU_LAW | \ - SNDRV_PCM_FORMAT_A_LAW) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_MU_LAW | \ + SNDRV_PCM_FMTBIT_A_LAW) static int zx_tdm_dai_probe(struct snd_soc_dai *dai) { |