[alsa-devel] [PATCH 0/5] ASoC: mediatek: Add basic PCM driver for MT8516
This patch series add a basic PCM driver for MediaTek MT8516 with only support for ADDA Playback & Recording for now.
Fabien Parent (5): ASoC: mediatek: make agent_disable, msb & hd fields optional dt-bindings: sound: Add MT8516 AFE PCM bindings ASoC: mediatek: Add MT8516 PCM driver ASoC: mediatek: mt8516: Add ADDA DAI driver ASoC: mediatek: mt8516: register ADDA DAI
.../bindings/sound/mt8516-afe-pcm.txt | 28 + sound/soc/mediatek/Kconfig | 10 + sound/soc/mediatek/Makefile | 1 + sound/soc/mediatek/common/mtk-afe-fe-dai.c | 23 +- sound/soc/mediatek/mt8516/Makefile | 7 + sound/soc/mediatek/mt8516/mt8516-afe-common.h | 18 + sound/soc/mediatek/mt8516/mt8516-afe-pcm.c | 794 ++++++++++++++++++ sound/soc/mediatek/mt8516/mt8516-afe-regs.h | 218 +++++ sound/soc/mediatek/mt8516/mt8516-dai-adda.c | 316 +++++++ 9 files changed, 1406 insertions(+), 9 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mt8516-afe-pcm.txt create mode 100644 sound/soc/mediatek/mt8516/Makefile create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-common.h create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-pcm.c create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-regs.h create mode 100644 sound/soc/mediatek/mt8516/mt8516-dai-adda.c
Not every SoC have the following registers: agent_disable_reg, msg_reg, and hd_reg. Make them optional in order to allow more SoC to use the common DAI FE code.
Signed-off-by: Fabien Parent fparent@baylibre.com --- sound/soc/mediatek/common/mtk-afe-fe-dai.c | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index cf4978be062f..f39f5d8c4244 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -47,10 +47,13 @@ int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16); + /* enable agent */ - mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg, - 1 << memif->data->agent_disable_shift, - 0 << memif->data->agent_disable_shift); + if (memif->data->agent_disable_shift >= 0) + mtk_regmap_update_bits(afe->regmap, + memif->data->agent_disable_reg, + 1 << memif->data->agent_disable_shift, + 0 << memif->data->agent_disable_shift);
snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
@@ -143,9 +146,10 @@ int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream, memif->phys_buf_addr + memif->buffer_size - 1);
/* set MSB to 33-bit */ - mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg, - 1 << memif->data->msb_shift, - msb_at_bit33 << memif->data->msb_shift); + if (memif->data->msb_shift >= 0) + mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg, + 1 << memif->data->msb_shift, + msb_at_bit33 << memif->data->msb_shift);
/* set channel */ if (memif->data->mono_shift >= 0) { @@ -269,9 +273,10 @@ int mtk_afe_fe_prepare(struct snd_pcm_substream *substream, break; }
- mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg, - 1 << memif->data->hd_shift, - hd_audio << memif->data->hd_shift); + if (memif->data->hd_shift >= 0) + mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg, + 1 << memif->data->hd_shift, + hd_audio << memif->data->hd_shift);
return 0; }
Add documentation for the bindings of the MT8516 AFE PCM driver.
Signed-off-by: Fabien Parent fparent@baylibre.com --- .../bindings/sound/mt8516-afe-pcm.txt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt8516-afe-pcm.txt
diff --git a/Documentation/devicetree/bindings/sound/mt8516-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt8516-afe-pcm.txt new file mode 100644 index 000000000000..c5fb3c55a7f4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8516-afe-pcm.txt @@ -0,0 +1,28 @@ +Mediatek AFE PCM controller for mt8516 + +Required properties: +- compatible: "mediatek,mt8516-audio" +- interrupts: should contain AFE interrupt +- clocks: Must contain an entry for each entry in clock-names +- clock-names: should have these clock names: + "top_pdn_audio", + "aud_dac_clk", + "aud_dac_predis_clk", + "aud_adc_clk"; + +Example: + + + afe: mt8516-afe-pcm@11140000 { + compatible = "mediatek,mt8516-audio"; + reg = <0 0x11140000 0 0x1000>; + interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_LOW>; + clocks = <&topckgen CLK_TOP_AUDIO>, + <&audiotop CLK_AUD_DAC>, + <&audiotop CLK_AUD_DAC_PREDIS>, + <&audiotop CLK_AUD_ADC>; + clock-names = "top_pdn_audio", + "aud_dac_clk", + "aud_dac_predis_clk", + "aud_adc_clk"; + };
On Thu, 2 May 2019 14:10:38 +0200, Fabien Parent wrote:
Add documentation for the bindings of the MT8516 AFE PCM driver.
Signed-off-by: Fabien Parent fparent@baylibre.com
.../bindings/sound/mt8516-afe-pcm.txt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt8516-afe-pcm.txt
Reviewed-by: Rob Herring robh@kernel.org
This commit adds the PCM driver for the MediaTek MT8516 SoC.
Signed-off-by: Fabien Parent fparent@baylibre.com --- sound/soc/mediatek/Kconfig | 10 + sound/soc/mediatek/Makefile | 1 + sound/soc/mediatek/mt8516/Makefile | 6 + sound/soc/mediatek/mt8516/mt8516-afe-pcm.c | 792 ++++++++++++++++++++ sound/soc/mediatek/mt8516/mt8516-afe-regs.h | 218 ++++++ 5 files changed, 1027 insertions(+) create mode 100644 sound/soc/mediatek/mt8516/Makefile create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-pcm.c create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-regs.h
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index b35410e4020e..f0bae957d475 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -116,6 +116,16 @@ config SND_SOC_MT8183 Select Y if you have such device. If unsure select "N".
+config SND_SOC_MT8516 + tristate "ASoC support for Mediatek MT8516 chip" + depends on ARCH_MEDIATEK + select SND_SOC_MEDIATEK + help + This adds ASoC platform driver support for Mediatek MT8516 chip + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + config SND_SOC_MTK_BTCVSD tristate "ALSA BT SCO CVSD/MSBC Driver" help diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 76032cae6d51..a48500d69a8b 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ obj-$(CONFIG_SND_SOC_MT8183) += mt8183/ +obj-$(CONFIG_SND_SOC_MT8516) += mt8516/ diff --git a/sound/soc/mediatek/mt8516/Makefile b/sound/soc/mediatek/mt8516/Makefile new file mode 100644 index 000000000000..6e49b01d02c2 --- /dev/null +++ b/sound/soc/mediatek/mt8516/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +snd-soc-mt8516-afe-objs := \ + mt8516-afe-pcm.o + +obj-$(CONFIG_SND_SOC_MT8516) += snd-soc-mt8516-afe.o diff --git a/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c b/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c new file mode 100644 index 000000000000..84fbb5dbbd14 --- /dev/null +++ b/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 BayLibre, SAS + * Copyright (c) 2019 MediaTek, Inc + * Author: Fabien Parent fparent@baylibre.com + */ + +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <linux/module.h> +#include <linux/of.h> + +#include "mt8516-afe-regs.h" + +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" +#include "../common/mtk-base-afe.h" + +enum { + MT8516_AFE_MEMIF_DL1, + MT8516_AFE_MEMIF_DL2, + MT8516_AFE_MEMIF_VUL, + MT8516_AFE_MEMIF_DAI, + MT8516_AFE_MEMIF_AWB, + MT8516_AFE_MEMIF_MOD_DAI, + MT8516_AFE_MEMIF_HDMI, + MT8516_AFE_MEMIF_TDM_IN, + MT8516_AFE_MEMIF_MULTILINE_IN, + MT8516_AFE_MEMIF_NUM, +}; + +enum { + MT8516_AFE_IRQ_1 = 0, + MT8516_AFE_IRQ_2, + MT8516_AFE_IRQ_5, /* dedicated for HDMI */ + MT8516_AFE_IRQ_7, + MT8516_AFE_IRQ_10, /* dedicated for TDM IN */ + MT8516_AFE_IRQ_13, /* dedicated for ULM*/ + MT8516_AFE_IRQ_NUM +}; + +struct mt8516_afe_rate { + unsigned int rate; + unsigned int regvalue; +}; + +static const struct mt8516_afe_rate mt8516_afe_i2s_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 11025, .regvalue = 1 }, + { .rate = 12000, .regvalue = 2 }, + { .rate = 16000, .regvalue = 4 }, + { .rate = 22050, .regvalue = 5 }, + { .rate = 24000, .regvalue = 6 }, + { .rate = 32000, .regvalue = 8 }, + { .rate = 44100, .regvalue = 9 }, + { .rate = 48000, .regvalue = 10 }, + { .rate = 88000, .regvalue = 11 }, + { .rate = 96000, .regvalue = 12 }, + { .rate = 176400, .regvalue = 13 }, + { .rate = 192000, .regvalue = 14 }, +}; + +static int mt8516_afe_i2s_fs(struct snd_pcm_substream *substream, + unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt8516_afe_i2s_rates); i++) + if (mt8516_afe_i2s_rates[i].rate == sample_rate) + return mt8516_afe_i2s_rates[i].regvalue; + + return -EINVAL; +} + + +static int mt8516_afe_irq_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + return mt8516_afe_i2s_fs(substream, rate); +} + +static const unsigned int mt8516_afe_backup_list[] = { + AUDIO_TOP_CON0, + AUDIO_TOP_CON1, + AUDIO_TOP_CON3, + AFE_CONN0, + AFE_CONN1, + AFE_CONN2, + AFE_CONN3, + AFE_CONN5, + AFE_CONN_24BIT, + AFE_I2S_CON, + AFE_I2S_CON1, + AFE_I2S_CON2, + AFE_I2S_CON3, + AFE_ADDA_PREDIS_CON0, + AFE_ADDA_PREDIS_CON1, + AFE_ADDA_DL_SRC2_CON0, + AFE_ADDA_DL_SRC2_CON1, + AFE_ADDA_UL_SRC_CON0, + AFE_ADDA_UL_SRC_CON0, + AFE_ADDA_NEWIF_CFG1, + AFE_ADDA_TOP_CON0, + AFE_ADDA_UL_DL_CON0, + AFE_MEMIF_PBUF_SIZE, + AFE_MEMIF_PBUF2_SIZE, + AFE_DAC_CON0, + AFE_DAC_CON1, + AFE_DL1_BASE, + AFE_DL1_END, + AFE_DL2_BASE, + AFE_DL2_END, + AFE_VUL_BASE, + AFE_VUL_END, + AFE_AWB_BASE, + AFE_AWB_END, + AFE_DAI_BASE, + AFE_DAI_END, + AFE_HDMI_OUT_BASE, + AFE_HDMI_OUT_END, + AFE_HDMI_IN_2CH_BASE, + AFE_HDMI_IN_2CH_END, +}; + +static const struct snd_pcm_hardware mt8516_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID, + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 256, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 256, + .fifo_size = 0, +}; + +static const struct snd_kcontrol_new mt8516_afe_o03_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN1, 23, 1, 0), +}; + +static const struct snd_kcontrol_new mt8516_afe_o04_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN2, 8, 1, 0), +}; + +static const struct snd_kcontrol_new mt8516_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN5, 8, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0), +}; + +static const struct snd_kcontrol_new mt8516_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN5, 13, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget mt8516_memif_widgets[] = { + /* inter-connections */ + SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I07", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I08", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, + mt8516_afe_o03_mix, ARRAY_SIZE(mt8516_afe_o03_mix)), + SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, + mt8516_afe_o04_mix, ARRAY_SIZE(mt8516_afe_o04_mix)), + SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, + mt8516_afe_o09_mix, ARRAY_SIZE(mt8516_afe_o09_mix)), + SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, + mt8516_afe_o10_mix, ARRAY_SIZE(mt8516_afe_o10_mix)), +}; + +static const struct snd_soc_dapm_route mt8516_memif_routes[] = { + /* downlink */ + {"I05", NULL, "DL1"}, + {"I06", NULL, "DL1"}, + {"I07", NULL, "DL2"}, + {"I08", NULL, "DL2"}, + {"O03", "I05 Switch", "I05"}, + {"O04", "I06 Switch", "I06"}, + {"O03", "I07 Switch", "I07"}, + {"O04", "I08 Switch", "I08"}, + + /* uplink */ + {"I03", NULL, "AIN Mux"}, + {"I04", NULL, "AIN Mux"}, + + {"O09", "I03 Switch", "I03"}, + {"O10", "I04 Switch", "I04"}, + {"VUL", NULL, "O09"}, + {"VUL", NULL, "O10"}, +}; + +static struct mtk_base_irq_data mt8516_irq_data[MT8516_AFE_IRQ_NUM] = { + [MT8516_AFE_IRQ_1] = { + .id = MT8516_AFE_IRQ_1, + .irq_cnt_reg = AFE_IRQ_CNT1, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = 4, + .irq_fs_maskbit = 0xf, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = 0, + .irq_clr_reg = AFE_IRQ_CLR, + .irq_clr_shift = 0, + }, + [MT8516_AFE_IRQ_2] = { + .id = MT8516_AFE_IRQ_2, + .irq_cnt_reg = AFE_IRQ_CNT2, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = 8, + .irq_fs_maskbit = 0xf, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = 1, + .irq_clr_reg = AFE_IRQ_CLR, + .irq_clr_shift = 1, + }, + [MT8516_AFE_IRQ_5] = { + .id = MT8516_AFE_IRQ_5, + .irq_cnt_reg = AFE_IRQ_CNT5, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = -1, + .irq_fs_shift = -1, + .irq_en_reg = AFE_IRQ_MCU_CON2, + .irq_en_shift = 3, + .irq_clr_reg = AFE_IRQ_CLR, + .irq_clr_shift = 4, + }, + [MT8516_AFE_IRQ_7] = { + .id = MT8516_AFE_IRQ_7, + .irq_cnt_reg = AFE_IRQ_CNT7, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0xf, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = 14, + .irq_clr_reg = AFE_IRQ_CLR, + .irq_clr_shift = 6, + }, + [MT8516_AFE_IRQ_10] = { + .id = MT8516_AFE_IRQ_10, + .irq_cnt_reg = AFE_IRQ_CNT10, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = -1, + .irq_fs_shift = -1, + .irq_en_reg = AFE_IRQ_MCU_CON2, + .irq_en_shift = 4, + .irq_clr_reg = AFE_IRQ_CLR, + .irq_clr_shift = 9, + }, + [MT8516_AFE_IRQ_13] = { + .id = MT8516_AFE_IRQ_13, + .irq_cnt_reg = AFE_IRQ_CNT13, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0x3ffff, + .irq_fs_reg = -1, + .irq_fs_shift = -1, + .irq_en_reg = AFE_IRQ_MCU_CON2, + .irq_en_shift = 7, + .irq_clr_reg = AFE_IRQ_CLR, + .irq_clr_shift = 12, + }, +}; + +static struct mtk_base_afe_irq mt8516_irqs[MT8516_AFE_IRQ_NUM] = { + { .irq_data = &mt8516_irq_data[MT8516_AFE_IRQ_1] }, + { .irq_data = &mt8516_irq_data[MT8516_AFE_IRQ_2] }, + { .irq_data = &mt8516_irq_data[MT8516_AFE_IRQ_5] }, + { .irq_data = &mt8516_irq_data[MT8516_AFE_IRQ_7] }, + { .irq_data = &mt8516_irq_data[MT8516_AFE_IRQ_10] }, + { .irq_data = &mt8516_irq_data[MT8516_AFE_IRQ_13] }, +}; + +static struct mtk_base_memif_data mt8516_memif_data[MT8516_AFE_MEMIF_NUM] = { + [MT8516_AFE_MEMIF_DL1] = { + .name = "DL1", + .id = MT8516_AFE_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 0, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = 21, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 1, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_DL2] = { + .name = "DL2", + .id = MT8516_AFE_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 4, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = 22, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 2, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_VUL] = { + .name = "VUL", + .id = MT8516_AFE_MEMIF_VUL, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 16, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = 27, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 3, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_DAI] = { + .name = "DAI", + .id = MT8516_AFE_MEMIF_DAI, + .reg_ofs_base = AFE_DAI_BASE, + .reg_ofs_cur = AFE_DAI_CUR, + .fs_reg = AFE_DAC_CON0, + .fs_shift = 24, + .fs_maskbit = 0x3, + .mono_reg = AFE_DAC_CON1, + .mono_shift = -1, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 4, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_AWB] = { + .name = "AWB", + .id = MT8516_AFE_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 12, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = 24, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 6, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_MOD_DAI] = { + .name = "MOD_DAI", + .id = MT8516_AFE_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_PCM_BASE, + .reg_ofs_cur = AFE_MOD_PCM_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = 30, + .fs_maskbit = 0x3, + .mono_reg = AFE_DAC_CON1, + .mono_shift = -1, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 7, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_HDMI] = { + .name = "HDMI", + .id = MT8516_AFE_MEMIF_HDMI, + .reg_ofs_base = AFE_HDMI_OUT_BASE, + .reg_ofs_cur = AFE_HDMI_OUT_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = -1, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = -1, + .enable_reg = AFE_DAC_CON0, + .enable_shift = -1, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_TDM_IN] = { + .name = "TDM_IN", + .id = MT8516_AFE_MEMIF_TDM_IN, + .reg_ofs_base = AFE_HDMI_IN_2CH_BASE, + .reg_ofs_cur = AFE_HDMI_IN_2CH_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = -1, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = -1, + .enable_reg = AFE_DAC_CON0, + .enable_shift = -1, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, + [MT8516_AFE_MEMIF_MULTILINE_IN] = { + .name = "ULM", + .id = MT8516_AFE_MEMIF_MULTILINE_IN, + .reg_ofs_base = SPDIFIN_BASE_ADR, + .reg_ofs_cur = SPDIFIN_CUR_ADR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = -1, + .fs_maskbit = 0xf, + .mono_reg = AFE_DAC_CON1, + .mono_shift = -1, + .enable_reg = AFE_DAC_CON0, + .enable_shift = -1, + .hd_shift = -1, + .msb_shift = -1, + .agent_disable_shift = -1, + }, +}; + +struct mtk_base_afe_memif mt8516_memif[] = { + [MT8516_AFE_MEMIF_DL1] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_DL1], + .irq_usage = MT8516_AFE_IRQ_1, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_DL2] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_DL2], + .irq_usage = MT8516_AFE_IRQ_7, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_VUL] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_VUL], + .irq_usage = MT8516_AFE_IRQ_2, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_DAI] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_DAI], + .irq_usage = MT8516_AFE_IRQ_2, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_AWB] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_AWB], + .irq_usage = MT8516_AFE_IRQ_2, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_MOD_DAI] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_MOD_DAI], + .irq_usage = MT8516_AFE_IRQ_2, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_HDMI] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_HDMI], + .irq_usage = MT8516_AFE_IRQ_5, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_TDM_IN] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_TDM_IN], + .irq_usage = MT8516_AFE_IRQ_10, + .const_irq = 1, + }, + [MT8516_AFE_MEMIF_MULTILINE_IN] = { + .data = &mt8516_memif_data[MT8516_AFE_MEMIF_MULTILINE_IN], + .data = &mt8516_memif_data[8], + .irq_usage = MT8516_AFE_IRQ_13, + .const_irq = 1, + }, +}; + +static const struct regmap_config mt8516_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ABB_AFE_SDM_TEST, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t mt8516_afe_irq_handler(int irq, void *dev_id) +{ + struct mtk_base_afe *afe = dev_id; + unsigned int reg_value; + unsigned int memif_status; + int i, ret; + + ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); + if (ret) { + reg_value = AFE_IRQ_STATUS_BITS; + goto exit_irq; + } + + ret = regmap_read(afe->regmap, AFE_DAC_CON0, &memif_status); + if (ret) { + reg_value = AFE_IRQ_STATUS_BITS; + goto exit_irq; + } + + for (i = 0; i < MT8516_AFE_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + struct snd_pcm_substream *substream = memif->substream; + unsigned int irq_clr_shift = + afe->irqs[memif->irq_usage].irq_data->irq_clr_shift; + unsigned int enable_shift = memif->data->enable_shift; + + if (!substream) + continue; + + if (!(reg_value & (1 << irq_clr_shift))) + continue; + + if (enable_shift >= 0 && !((1 << enable_shift) & memif_status)) + continue; + + snd_pcm_period_elapsed(substream); + } + +exit_irq: + regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); + + return IRQ_HANDLED; +} + +static struct snd_soc_dai_driver mt8516_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", + .id = MT8516_AFE_MEMIF_DL1, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mtk_afe_fe_ops, + }, { + .name = "DL2", + .id = MT8516_AFE_MEMIF_DL2, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .playback = { + .stream_name = "DL2", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mtk_afe_fe_ops, + }, { + .name = "VUL", + .id = MT8516_AFE_MEMIF_VUL, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .capture = { + .stream_name = "VUL", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mtk_afe_fe_ops, + }, { + .name = "DAI", + .id = MT8516_AFE_MEMIF_DAI, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .capture = { + .stream_name = "DAI", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_fe_ops, + }, { + .name = "HDMI", + .id = MT8516_AFE_MEMIF_HDMI, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .playback = { + .stream_name = "HDMI", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mtk_afe_fe_ops, + }, { + .name = "TDM_IN", + .id = MT8516_AFE_MEMIF_TDM_IN, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .capture = { + .stream_name = "TDM_IN", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mtk_afe_fe_ops, + }, { + .name = "ULM", + .id = MT8516_AFE_MEMIF_MULTILINE_IN, + .suspend = mtk_afe_dai_suspend, + .resume = mtk_afe_dai_resume, + .capture = { + .stream_name = "MULTILINE_IN", + .channels_min = 1, + .channels_max = 8, + .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_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mtk_afe_fe_ops, + }, +}; + +static int mt8516_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 = mt8516_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt8516_memif_dai_driver); + + dai->dapm_widgets = mt8516_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt8516_memif_widgets); + dai->dapm_routes = mt8516_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt8516_memif_routes); + + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt8516_dai_memif_register, +}; + +static int mt8516_afe_component_probe(struct snd_soc_component *component) +{ + return mtk_afe_add_sub_dai_control(component); +} + +static const struct snd_soc_component_driver mt8516_afe_component = { + .name = AFE_PCM_NAME, + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, + .probe = mt8516_afe_component_probe, +}; + +static int mt8516_afe_pcm_dev_probe(struct platform_device *pdev) +{ + int ret, i; + unsigned int irq_id; + struct mtk_base_afe *afe; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + platform_set_drvdata(pdev, afe); + + afe->dev = &pdev->dev; + + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(afe->dev, "np %s no irq\n", np->name); + return -ENXIO; + } + + ret = devm_request_irq(afe->dev, irq_id, mt8516_afe_irq_handler, + 0, "Afe_ISR_Handle", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt8516_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + afe->reg_back_up_list = &mt8516_afe_backup_list[0]; + afe->reg_back_up_list_num = ARRAY_SIZE(mt8516_afe_backup_list); + + /* init sub_dais */ + 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, + "Failed to register dai register %d, ret %d\n", + i, ret); + return ret; + } + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) { + dev_warn(afe->dev, "Failed to combine sub-dais, ret %d\n", ret); + return ret; + } + + afe->mtk_afe_hardware = &mt8516_afe_hardware; + + afe->irqs = mt8516_irqs; + afe->irq_fs = mt8516_afe_irq_fs; + + afe->memif = &mt8516_memif[0]; + afe->memif_size = ARRAY_SIZE(mt8516_memif); + afe->memif_fs = mt8516_afe_i2s_fs; + + ret = devm_snd_soc_register_component(&pdev->dev, + &mt8516_afe_component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) + return ret; + + return 0; +} + +static int mt8516_afe_pcm_dev_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id mt8516_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8516-audio", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8516_afe_pcm_dt_match); + +static struct platform_driver mt8516_afe_pcm_driver = { + .driver = { + .name = "mtk-afe-pcm", + .of_match_table = mt8516_afe_pcm_dt_match, + }, + .probe = mt8516_afe_pcm_dev_probe, + .remove = mt8516_afe_pcm_dev_remove, +}; + +module_platform_driver(mt8516_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Fabien Parent fparent@baylibre.com"); diff --git a/sound/soc/mediatek/mt8516/mt8516-afe-regs.h b/sound/soc/mediatek/mt8516/mt8516-afe-regs.h new file mode 100644 index 000000000000..0edb19cfecf4 --- /dev/null +++ b/sound/soc/mediatek/mt8516/mt8516-afe-regs.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Copyright (c) 2019 MediaTek, Inc + * Author: Fabien Parent fparent@baylibre.com + */ + +#ifndef _MT8516_AFE_REGS_H_ +#define _MT8516_AFE_REGS_H_ + +#include <linux/bitops.h> + +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON1 0x0004 +#define AUDIO_TOP_CON3 0x000c +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON 0x0018 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_I2S_CON3 0x004c +#define AFE_DAIBT_CON0 0x001c +#define AFE_MRGIF_CON 0x003c +#define AFE_CONN_24BIT 0x006c + +#define AFE_CONN0 0x0020 +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN3 0x002C +#define AFE_CONN4 0x0030 +#define AFE_CONN5 0x005C + +/* Memory interface */ +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL1_END 0x0048 +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_DL2_END 0x0058 +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_END 0x0078 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_CUR 0x008c +#define AFE_VUL_END 0x0088 +#define AFE_DAI_BASE 0x0090 +#define AFE_DAI_END 0x0098 +#define AFE_DAI_CUR 0x009c +#define AFE_MOD_PCM_BASE 0x0330 +#define AFE_MOD_PCM_END 0x0338 +#define AFE_MOD_PCM_CUR 0x033c +#define AFE_HDMI_OUT_BASE 0x0374 +#define AFE_HDMI_OUT_CUR 0x0378 +#define AFE_HDMI_OUT_END 0x037c + +#define AFE_MEMIF_MSB 0x00cc +#define AFE_MEMIF_MON0 0x00d0 +#define AFE_MEMIF_MON1 0x00d4 +#define AFE_MEMIF_MON2 0x00d8 +#define AFE_MEMIF_MON3 0x00dc + +#define AFE_ADDA_DL_SRC2_CON0 0x0108 +#define AFE_ADDA_DL_SRC2_CON1 0x010c +#define AFE_ADDA_UL_SRC_CON0 0x0114 +#define AFE_ADDA_UL_SRC_CON1 0x0118 +#define AFE_ADDA_TOP_CON0 0x0120 +#define AFE_ADDA_UL_DL_CON0 0x0124 +#define AFE_ADDA_NEWIF_CFG0 0x0138 +#define AFE_ADDA_NEWIF_CFG1 0x013c +#define AFE_ADDA_PREDIS_CON0 0x0260 +#define AFE_ADDA_PREDIS_CON1 0x0264 + +#define AFE_HDMI_OUT_CON0 0x0370 + +#define AFE_IRQ_MCU_CON 0x03a0 +#define AFE_IRQ_STATUS 0x03a4 +#define AFE_IRQ_CLR 0x03a8 +#define AFE_IRQ_CNT1 0x03ac +#define AFE_IRQ_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_CNT5 0x03bc +#define AFE_IRQ_CNT7 0x03dc +#define AFE_IRQ_CNT13 0x0408 +#define AFE_IRQ1_MCU_CNT_MON 0x03c0 +#define AFE_IRQ2_MCU_CNT_MON 0x03c4 +#define AFE_IRQ_MCU_CON2 0x03f8 + +#define AFE_MEMIF_PBUF_SIZE 0x03d8 +#define AFE_MEMIF_PBUF2_SIZE 0x03ec + +#define AFE_ASRC_CON0 0x0500 + +#define AFE_ASRC_CON13 0x0550 +#define AFE_ASRC_CON14 0x0554 +#define AFE_ASRC_CON15 0x0558 +#define AFE_ASRC_CON16 0x055c +#define AFE_ASRC_CON17 0x0560 +#define AFE_ASRC_CON18 0x0564 +#define AFE_ASRC_CON19 0x0568 +#define AFE_ASRC_CON20 0x056c +#define AFE_ASRC_CON21 0x0570 + +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c + +#define AFE_TDM_IN_CON1 0x0588 +#define AFE_TDM_IN_MON2 0x0594 +#define AFE_IRQ_CNT10 0x08dc + +#define AFE_HDMI_IN_2CH_CON0 0x09c0 +#define AFE_HDMI_IN_2CH_BASE 0x09c4 +#define AFE_HDMI_IN_2CH_END 0x09c8 +#define AFE_HDMI_IN_2CH_CUR 0x09cc + +#define AFE_MEMIF_MON15 0x0d7c +#define ABB_AFE_SDM_TEST 0x0f4c + +#define AFE_IRQ_STATUS_BITS 0x13ff + +/* AFE_I2S_CON (0x0018) */ +#define AFE_I2S_CON_PHASE_SHIFT_FIX BIT(31) +#define AFE_I2S_CON_BCK_INV BIT(29) +#define AFE_I2S_CON_FROM_IO_MUX BIT(28) +#define AFE_I2S_CON_LOW_JITTER_CLK BIT(12) +#define AFE_I2S_CON_LRCK_INV BIT(5) +#define AFE_I2S_CON_FORMAT_I2S BIT(3) +#define AFE_I2S_CON_SRC_SLAVE BIT(2) +#define AFE_I2S_CON_WLEN_32BIT BIT(1) +#define AFE_I2S_CON_EN BIT(0) + +/* AFE_CONN1 (0x0024) */ +#define AFE_CONN1_I03_O03_S BIT(19) + +/* AFE_CONN2 (0x0028) */ +#define AFE_CONN2_I04_O04_S BIT(4) +#define AFE_CONN2_I03_O04_S BIT(3) + +/* AFE_I2S_CON1 (0x0034) */ +#define AFE_I2S_CON1_I2S2_TO_PAD (1 << 18) +#define AFE_I2S_CON1_TDMOUT_TO_PAD (0 << 18) +#define AFE_I2S_CON1_TDMOUT_MUX_MASK GENMASK(18, 18) +#define AFE_I2S_CON1_LOW_JITTER_CLK BIT(12) +#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON1_FORMAT_I2S BIT(3) +#define AFE_I2S_CON1_WLEN_32BIT BIT(1) +#define AFE_I2S_CON1_EN BIT(0) + +/* AFE_I2S_CON2 (0x0038) */ +#define AFE_I2S_CON2_LOW_JITTER_CLK BIT(12) +#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON2_FORMAT_I2S BIT(3) +#define AFE_I2S_CON2_WLEN_32BIT BIT(1) +#define AFE_I2S_CON2_EN BIT(0) + +/* AFE_I2S_CON3 (0x004C) */ +#define AFE_I2S_CON3_LOW_JITTER_CLK BIT(12) +#define AFE_I2S_CON3_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON3_FORMAT_I2S BIT(3) +#define AFE_I2S_CON3_WLEN_32BIT BIT(1) +#define AFE_I2S_CON3_EN BIT(0) + +/* AFE_CONN_24BIT (0x006c) */ +#define AFE_CONN_24BIT_O10 BIT(10) +#define AFE_CONN_24BIT_O09 BIT(9) +#define AFE_CONN_24BIT_O06 BIT(6) +#define AFE_CONN_24BIT_O05 BIT(5) +#define AFE_CONN_24BIT_O04 BIT(4) +#define AFE_CONN_24BIT_O03 BIT(3) +#define AFE_CONN_24BIT_O02 BIT(2) +#define AFE_CONN_24BIT_O01 BIT(1) +#define AFE_CONN_24BIT_O00 BIT(0) + +/* AFE_ADDA_UL_SRC_CON0 */ +#define AFE_ADDA_UL_RATE_CH1_SHIFT 17 +#define AFE_ADDA_UL_RATE_CH1_MASK 0x3 +#define AFE_ADDA_UL_RATE_CH2_SHIFT 19 +#define AFE_ADDA_UL_RATE_CH2_MASK 0x3 + +/* AFE_ADDA_DL_SRC2_CON0 (0x0108) */ +#define AFE_ADDA_DL_8X_UPSAMPLE (BIT(25) | BIT(24)) +#define AFE_ADDA_DL_MUTE_OFF (BIT(12) | BIT(11)) +#define AFE_ADDA_DL_VOICE_DATA BIT(5) +#define AFE_ADDA_DL_DEGRADE_GAIN BIT(1) +#define AFE_ADDA_DL_RATE_SHIFT 28 + +/* AFE_ASRC_CON0 (0x0500) */ +#define AFE_ASRC_CON0_ASM_ON BIT(0) +#define AFE_ASRC_CON0_STR_CLR_MASK GENMASK(6, 4) +#define AFE_ASRC_CON0_CLR_TX (0x1 << 4) +#define AFE_ASRC_CON0_CLR_RX (0x2 << 4) +#define AFE_ASRC_CON0_CLR_I2S (0x4 << 4) + +/* AFE_ASRC_CON13 (0x0550) */ +#define AFE_ASRC_CON13_16BIT BIT(19) +#define AFE_ASRC_CON13_MONO BIT(16) + +/* AFE_ASRC_CON16 (0x055c) */ +#define AFE_ASRC_CON16_FC2_CYCLE_MASK GENMASK(31, 16) +#define AFE_ASRC_CON16_FC2_CYCLE(x) (((x) - 1) << 16) +#define AFE_ASRC_CON16_FC2_AUTO_RST BIT(14) +#define AFE_ASRC_CON16_TUNE_FREQ5 BIT(12) +#define AFE_ASRC_CON16_COMP_FREQ_EN BIT(11) +#define AFE_ASRC_CON16_FC2_SEL GENMASK(9, 8) +#define AFE_ASRC_CON16_FC2_I2S_IN (0x1 << 8) +#define AFE_ASRC_CON16_FC2_DGL_BYPASS BIT(7) +#define AFE_ASRC_CON16_FC2_AUTO_RESTART BIT(2) +#define AFE_ASRC_CON16_FC2_FREQ BIT(1) +#define AFE_ASRC_CON16_FC2_EN BIT(0) + +/* AFE_ADDA_NEWIF_CFG0 (0x0138) */ +#define AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT 10 +#define AFE_ADDA_NEWIF_ADC_VOICE_MODE_CLR (0x3 << 10) + +/* AFE_SPDIF_IN */ +#define SPDIFIN_BASE_ADR (0x0994) +#define SPDIFIN_CUR_ADR (0x09B8) + +#endif
On Thu, May 02, 2019 at 02:10:39PM +0200, Fabien Parent wrote:
+static irqreturn_t mt8516_afe_irq_handler(int irq, void *dev_id) +{
- struct mtk_base_afe *afe = dev_id;
- unsigned int reg_value;
- unsigned int memif_status;
- int i, ret;
- ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value);
- if (ret) {
reg_value = AFE_IRQ_STATUS_BITS;
goto exit_irq;
- }
...
+exit_irq:
- regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
- return IRQ_HANDLED;
+}
This unconditionally says it handled an interrupt regardless of what happened. This means that the interrupt line can't be shared and that the error handling code in the generic interrupt subsystem can't tell if something goes wrong and the interrupt gets stuck.
- ret = devm_request_irq(afe->dev, irq_id, mt8516_afe_irq_handler,
0, "Afe_ISR_Handle", (void *)afe);
- if (ret) {
dev_err(afe->dev, "could not request_irq\n");
return ret;
- }
Are you sure the interrupt handler can safely use managed resources, especially given...
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(afe->base_addr))
return PTR_ERR(afe->base_addr);
- afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
&mt8516_afe_regmap_config);
- if (IS_ERR(afe->regmap))
return PTR_ERR(afe->regmap);
...that things like the register map and the I/O resources for the chip are allocated after and therefore freed before before the interrupt is freed. Normally the interrupt should be one of the last things to be allocated.
+static int mt8516_afe_pcm_dev_remove(struct platform_device *pdev) +{
- return 0;
+}
In general if functions can legitimately be empty they should just be omitted, if they are required that usually means they have to do something.
Add the ADDA DAI driver for the MediaTek MT8516 SoC.
Signed-off-by: Fabien Parent fparent@baylibre.com --- sound/soc/mediatek/mt8516/Makefile | 3 +- sound/soc/mediatek/mt8516/mt8516-afe-common.h | 18 + sound/soc/mediatek/mt8516/mt8516-dai-adda.c | 316 ++++++++++++++++++ 3 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-common.h create mode 100644 sound/soc/mediatek/mt8516/mt8516-dai-adda.c
diff --git a/sound/soc/mediatek/mt8516/Makefile b/sound/soc/mediatek/mt8516/Makefile index 6e49b01d02c2..dc05ab45560d 100644 --- a/sound/soc/mediatek/mt8516/Makefile +++ b/sound/soc/mediatek/mt8516/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0
snd-soc-mt8516-afe-objs := \ - mt8516-afe-pcm.o + mt8516-afe-pcm.o \ + mt8516-dai-adda.o
obj-$(CONFIG_SND_SOC_MT8516) += snd-soc-mt8516-afe.o diff --git a/sound/soc/mediatek/mt8516/mt8516-afe-common.h b/sound/soc/mediatek/mt8516/mt8516-afe-common.h new file mode 100644 index 000000000000..e70877c044a4 --- /dev/null +++ b/sound/soc/mediatek/mt8516/mt8516-afe-common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Fabien Parent fparent@baylibre.com + */ + +#ifndef _MT8516_AFE_COMMON_H_ +#define _MT8516_AFE_COMMON_H_ + +#include "../common/mtk-base-afe.h" + +enum { + MT8516_AFE_BE_ADDA, +}; + +int mt8516_dai_adda_register(struct mtk_base_afe *afe); + +#endif diff --git a/sound/soc/mediatek/mt8516/mt8516-dai-adda.c b/sound/soc/mediatek/mt8516/mt8516-dai-adda.c new file mode 100644 index 000000000000..76e53772d784 --- /dev/null +++ b/sound/soc/mediatek/mt8516/mt8516-dai-adda.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 BayLibre, SAS + * Copyright (c) 2019 MediaTek, Inc + * Author: Fabien Parent fparent@baylibre.com + */ + +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "mt8516-afe-common.h" +#include "mt8516-afe-regs.h" + +enum { + MTK_AFE_ADDA_DL_RATE_8K = 0, + MTK_AFE_ADDA_DL_RATE_11K = 1, + MTK_AFE_ADDA_DL_RATE_12K = 2, + MTK_AFE_ADDA_DL_RATE_16K = 3, + MTK_AFE_ADDA_DL_RATE_22K = 4, + MTK_AFE_ADDA_DL_RATE_24K = 5, + MTK_AFE_ADDA_DL_RATE_32K = 6, + MTK_AFE_ADDA_DL_RATE_44K = 7, + MTK_AFE_ADDA_DL_RATE_48K = 8, +}; + +enum { + MTK_AFE_ADDA_UL_RATE_8K = 0, + MTK_AFE_ADDA_UL_RATE_16K = 1, + MTK_AFE_ADDA_UL_RATE_32K = 2, + MTK_AFE_ADDA_UL_RATE_48K = 3, +}; + +static int mt8516_afe_setup_i2s(struct mtk_base_afe *afe, + struct snd_pcm_substream *substream, + unsigned int rate, int bit_width) +{ + int fs = afe->memif_fs(substream, rate); + unsigned int val; + + if (bit_width > 16) + val |= AFE_I2S_CON1_WLEN_32BIT; + + if (fs < 0) + return -EINVAL; + + val = AFE_I2S_CON1_I2S2_TO_PAD | + AFE_I2S_CON1_LOW_JITTER_CLK | + AFE_I2S_CON1_RATE(fs) | + AFE_I2S_CON1_FORMAT_I2S | + AFE_I2S_CON1_EN; + + regmap_write(afe->regmap, AFE_I2S_CON1, val); + + return 0; +} + +static int mt8516_afe_setup_adda_dl(struct mtk_base_afe *afe, unsigned int rate) +{ + unsigned int val = AFE_ADDA_DL_8X_UPSAMPLE | + AFE_ADDA_DL_MUTE_OFF | + AFE_ADDA_DL_DEGRADE_GAIN; + + if (rate == 8000 || rate == 16000) + val |= AFE_ADDA_DL_VOICE_DATA; + + switch (rate) { + case 8000: + val |= MTK_AFE_ADDA_DL_RATE_8K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 11025: + val |= MTK_AFE_ADDA_DL_RATE_11K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 12000: + val |= MTK_AFE_ADDA_DL_RATE_12K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 16000: + val |= MTK_AFE_ADDA_DL_RATE_16K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 22050: + val |= MTK_AFE_ADDA_DL_RATE_22K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 24000: + val |= MTK_AFE_ADDA_DL_RATE_24K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 32000: + val |= MTK_AFE_ADDA_DL_RATE_32K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 44100: + val |= MTK_AFE_ADDA_DL_RATE_44K << AFE_ADDA_DL_RATE_SHIFT; + break; + case 48000: + val |= MTK_AFE_ADDA_DL_RATE_48K << AFE_ADDA_DL_RATE_SHIFT; + break; + default: + return -EINVAL; + } + + regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0); + regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0); + regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, val); + regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, 0xf74f0000); + + return 0; +} + +static int mt8516_afe_setup_adda_ul(struct mtk_base_afe *afe, unsigned int rate) +{ + unsigned int val = 0; + unsigned int val2 = 0; + + switch (rate) { + case 8000: + val |= MTK_AFE_ADDA_UL_RATE_8K << AFE_ADDA_UL_RATE_CH1_SHIFT; + val |= MTK_AFE_ADDA_UL_RATE_8K << AFE_ADDA_UL_RATE_CH2_SHIFT; + val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT; + break; + case 16000: + val |= MTK_AFE_ADDA_UL_RATE_16K << AFE_ADDA_UL_RATE_CH1_SHIFT; + val |= MTK_AFE_ADDA_UL_RATE_16K << AFE_ADDA_UL_RATE_CH2_SHIFT; + val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT; + break; + case 32000: + val |= MTK_AFE_ADDA_UL_RATE_32K << AFE_ADDA_UL_RATE_CH1_SHIFT; + val |= MTK_AFE_ADDA_UL_RATE_32K << AFE_ADDA_UL_RATE_CH2_SHIFT; + val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT; + break; + case 48000: + val |= MTK_AFE_ADDA_UL_RATE_48K << AFE_ADDA_UL_RATE_CH1_SHIFT; + val |= MTK_AFE_ADDA_UL_RATE_48K << AFE_ADDA_UL_RATE_CH2_SHIFT; + val2 |= 3 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, + (AFE_ADDA_UL_RATE_CH1_MASK << AFE_ADDA_UL_RATE_CH1_SHIFT) || + (AFE_ADDA_UL_RATE_CH2_MASK << AFE_ADDA_UL_RATE_CH2_MASK), val); + regmap_update_bits(afe->regmap, AFE_ADDA_NEWIF_CFG1, + AFE_ADDA_NEWIF_ADC_VOICE_MODE_CLR, val2); + regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 1, 0); + + return 0; +} + +static void mt8516_afe_adda_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + unsigned int stream = substream->stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 1, 0); + regmap_update_bits(afe->regmap, AFE_I2S_CON1, 1, 0); + } else { + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 1, 0); + } + + regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 1, 0); +} + +static int mt8516_afe_adda_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + unsigned int width_val = params_width(params) > 16 ? + (AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04) : 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(afe->regmap, AFE_CONN_24BIT, + AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, width_val); + + return 0; +} + +static int mt8516_afe_adda_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + const unsigned int rate = substream->runtime->rate; + unsigned int stream = substream->stream; + int bit_width = snd_pcm_format_width(substream->runtime->format); + int ret; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = mt8516_afe_setup_adda_dl(afe, rate); + if (ret) + return ret; + + ret = mt8516_afe_setup_i2s(afe, substream, rate, bit_width); + if (ret) + return ret; + + regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 1, 1); + } else { + ret = mt8516_afe_setup_adda_ul(afe, rate); + if (ret) + return ret; + + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 1, 1); + } + + regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 1, 1); + + return 0; +} + +static const struct snd_soc_dai_ops mt8516_afe_adda_ops = { + .shutdown = mt8516_afe_adda_shutdown, + .hw_params = mt8516_afe_adda_hw_params, + .prepare = mt8516_afe_adda_prepare, +}; + +static const struct snd_kcontrol_new adda_o03_o04_enable_ctl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const char * const ain_text[] = { + "INT ADC", "EXT ADC" +}; + +static SOC_ENUM_SINGLE_DECL(ain_enum, AFE_ADDA_TOP_CON0, 0, ain_text); + +static const struct snd_kcontrol_new ain_mux = + SOC_DAPM_ENUM("AIN Source", ain_enum); + +enum { + SUPPLY_SEQ_ADDA_AFE_ON, +}; + +static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { + SND_SOC_DAPM_MUX("AIN Mux", SND_SOC_NOPM, 0, 0, &ain_mux), + + SND_SOC_DAPM_SWITCH("ADDA O03_O04", SND_SOC_NOPM, 0, 0, + &adda_o03_o04_enable_ctl), + + + SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON, + AFE_DAC_CON0, 0, 0, + NULL, 0), + + /* Clocks */ + SND_SOC_DAPM_CLOCK_SUPPLY("top_pdn_audio"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"), +}; + +static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { + /* playback */ + {"ADDA O03_O04", "Switch", "O03"}, + {"ADDA O03_O04", "Switch", "O04"}, + {"ADDA Playback", NULL, "ADDA O03_O04"}, + + /* capture */ + {"AIN Mux", "INT ADC", "ADDA Capture"}, + + /* enable */ + {"ADDA Playback", NULL, "ADDA Enable"}, + {"ADDA Capture", NULL, "ADDA Enable"}, + + /* clock */ + {"ADDA Playback", NULL, "aud_dac_clk"}, + {"ADDA Playback", NULL, "aud_dac_predis_clk"}, + {"ADDA Playback", NULL, "top_pdn_audio"}, + + {"ADDA Capture", NULL, "top_pdn_audio"}, + {"ADDA Capture", NULL, "aud_adc_clk"}, +}; + +static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { + { + .name = "ADDA", + .id = MT8516_AFE_BE_ADDA, + .playback = { + .stream_name = "ADDA Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "ADDA Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mt8516_afe_adda_ops, + }, +}; + +int mt8516_dai_adda_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 = 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; +}
Register the ADDA DAI driver into the MT8516 PCM driver.
Signed-off-by: Fabien Parent fparent@baylibre.com --- sound/soc/mediatek/mt8516/mt8516-afe-pcm.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c b/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c index 84fbb5dbbd14..e1fd9290dd8f 100644 --- a/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c +++ b/sound/soc/mediatek/mt8516/mt8516-afe-pcm.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/of.h>
+#include "mt8516-afe-common.h" #include "mt8516-afe-regs.h"
#include "../common/mtk-afe-platform-driver.h" @@ -670,6 +671,7 @@ static int mt8516_dai_memif_register(struct mtk_base_afe *afe) typedef int (*dai_register_cb)(struct mtk_base_afe *); static const dai_register_cb dai_register_cbs[] = { mt8516_dai_memif_register, + mt8516_dai_adda_register, };
static int mt8516_afe_component_probe(struct snd_soc_component *component)
participants (3)
-
Fabien Parent
-
Mark Brown
-
Rob Herring