165816025SJerome Brunet // SPDX-License-Identifier: GPL-2.0 265816025SJerome Brunet // 365816025SJerome Brunet // Copyright (c) 2020 BayLibre, SAS. 465816025SJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com> 565816025SJerome Brunet 665816025SJerome Brunet #include <linux/bitfield.h> 765816025SJerome Brunet #include <sound/pcm_params.h> 865816025SJerome Brunet #include <sound/soc.h> 965816025SJerome Brunet #include <sound/soc-dai.h> 1065816025SJerome Brunet 1165816025SJerome Brunet #include <dt-bindings/sound/meson-aiu.h> 1265816025SJerome Brunet #include "aiu.h" 1365816025SJerome Brunet #include "meson-codec-glue.h" 1465816025SJerome Brunet 1565816025SJerome Brunet #define CTRL_DIN_EN 15 1665816025SJerome Brunet #define CTRL_CLK_INV BIT(14) 1765816025SJerome Brunet #define CTRL_LRCLK_INV BIT(13) 1865816025SJerome Brunet #define CTRL_I2S_IN_BCLK_SRC BIT(11) 1965816025SJerome Brunet #define CTRL_DIN_LRCLK_SRC_SHIFT 6 2065816025SJerome Brunet #define CTRL_DIN_LRCLK_SRC (0x3 << CTRL_DIN_LRCLK_SRC_SHIFT) 2165816025SJerome Brunet #define CTRL_BCLK_MCLK_SRC GENMASK(5, 4) 2265816025SJerome Brunet #define CTRL_DIN_SKEW GENMASK(3, 2) 2365816025SJerome Brunet #define CTRL_I2S_OUT_LANE_SRC 0 2465816025SJerome Brunet 2565816025SJerome Brunet #define AIU_ACODEC_OUT_CHMAX 2 2665816025SJerome Brunet 2765816025SJerome Brunet static const char * const aiu_acodec_ctrl_mux_texts[] = { 2865816025SJerome Brunet "DISABLED", "I2S", "PCM", 2965816025SJerome Brunet }; 3065816025SJerome Brunet 3165816025SJerome Brunet static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, 3265816025SJerome Brunet struct snd_ctl_elem_value *ucontrol) 3365816025SJerome Brunet { 3465816025SJerome Brunet struct snd_soc_component *component = 3565816025SJerome Brunet snd_soc_dapm_kcontrol_component(kcontrol); 3665816025SJerome Brunet struct snd_soc_dapm_context *dapm = 3765816025SJerome Brunet snd_soc_dapm_kcontrol_dapm(kcontrol); 3865816025SJerome Brunet struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 3965816025SJerome Brunet unsigned int mux, changed; 4065816025SJerome Brunet 4165816025SJerome Brunet mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); 4265816025SJerome Brunet changed = snd_soc_component_test_bits(component, e->reg, 4365816025SJerome Brunet CTRL_DIN_LRCLK_SRC, 4465816025SJerome Brunet FIELD_PREP(CTRL_DIN_LRCLK_SRC, 4565816025SJerome Brunet mux)); 4665816025SJerome Brunet 4765816025SJerome Brunet if (!changed) 4865816025SJerome Brunet return 0; 4965816025SJerome Brunet 5065816025SJerome Brunet /* Force disconnect of the mux while updating */ 5165816025SJerome Brunet snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 5265816025SJerome Brunet 5365816025SJerome Brunet snd_soc_component_update_bits(component, e->reg, 5465816025SJerome Brunet CTRL_DIN_LRCLK_SRC | 5565816025SJerome Brunet CTRL_BCLK_MCLK_SRC, 5665816025SJerome Brunet FIELD_PREP(CTRL_DIN_LRCLK_SRC, mux) | 5765816025SJerome Brunet FIELD_PREP(CTRL_BCLK_MCLK_SRC, mux)); 5865816025SJerome Brunet 5965816025SJerome Brunet snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); 6065816025SJerome Brunet 612e3a0d1bSMark Brown return 1; 6265816025SJerome Brunet } 6365816025SJerome Brunet 6465816025SJerome Brunet static SOC_ENUM_SINGLE_DECL(aiu_acodec_ctrl_mux_enum, AIU_ACODEC_CTRL, 6565816025SJerome Brunet CTRL_DIN_LRCLK_SRC_SHIFT, 6665816025SJerome Brunet aiu_acodec_ctrl_mux_texts); 6765816025SJerome Brunet 6865816025SJerome Brunet static const struct snd_kcontrol_new aiu_acodec_ctrl_mux = 6965816025SJerome Brunet SOC_DAPM_ENUM_EXT("ACodec Source", aiu_acodec_ctrl_mux_enum, 7065816025SJerome Brunet snd_soc_dapm_get_enum_double, 7165816025SJerome Brunet aiu_acodec_ctrl_mux_put_enum); 7265816025SJerome Brunet 7365816025SJerome Brunet static const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable = 7465816025SJerome Brunet SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL, 7565816025SJerome Brunet CTRL_DIN_EN, 1, 0); 7665816025SJerome Brunet 7765816025SJerome Brunet static const struct snd_soc_dapm_widget aiu_acodec_ctrl_widgets[] = { 7865816025SJerome Brunet SND_SOC_DAPM_MUX("ACODEC SRC", SND_SOC_NOPM, 0, 0, 7965816025SJerome Brunet &aiu_acodec_ctrl_mux), 8065816025SJerome Brunet SND_SOC_DAPM_SWITCH("ACODEC OUT EN", SND_SOC_NOPM, 0, 0, 8165816025SJerome Brunet &aiu_acodec_ctrl_out_enable), 8265816025SJerome Brunet }; 8365816025SJerome Brunet 8465816025SJerome Brunet static int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream, 8565816025SJerome Brunet struct snd_pcm_hw_params *params, 8665816025SJerome Brunet struct snd_soc_dai *dai) 8765816025SJerome Brunet { 8865816025SJerome Brunet struct meson_codec_glue_input *data; 8965816025SJerome Brunet int ret; 9065816025SJerome Brunet 9165816025SJerome Brunet ret = meson_codec_glue_input_hw_params(substream, params, dai); 9265816025SJerome Brunet if (ret) 9365816025SJerome Brunet return ret; 9465816025SJerome Brunet 9565816025SJerome Brunet /* The glue will provide 1 lane out of the 4 to the output */ 9665816025SJerome Brunet data = meson_codec_glue_input_get_data(dai); 9765816025SJerome Brunet data->params.channels_min = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, 9865816025SJerome Brunet data->params.channels_min); 9965816025SJerome Brunet data->params.channels_max = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, 10065816025SJerome Brunet data->params.channels_max); 10165816025SJerome Brunet 10265816025SJerome Brunet return 0; 10365816025SJerome Brunet } 10465816025SJerome Brunet 10565816025SJerome Brunet static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = { 106*2d3155a9SKuninori Morimoto .probe = meson_codec_glue_input_dai_probe, 107*2d3155a9SKuninori Morimoto .remove = meson_codec_glue_input_dai_remove, 10865816025SJerome Brunet .hw_params = aiu_acodec_ctrl_input_hw_params, 10965816025SJerome Brunet .set_fmt = meson_codec_glue_input_set_fmt, 11065816025SJerome Brunet }; 11165816025SJerome Brunet 11265816025SJerome Brunet static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = { 11365816025SJerome Brunet .startup = meson_codec_glue_output_startup, 11465816025SJerome Brunet }; 11565816025SJerome Brunet 11665816025SJerome Brunet #define AIU_ACODEC_CTRL_FORMATS \ 11765816025SJerome Brunet (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 11865816025SJerome Brunet SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ 11965816025SJerome Brunet SNDRV_PCM_FMTBIT_S32_LE) 12065816025SJerome Brunet 12165816025SJerome Brunet #define AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \ 12265816025SJerome Brunet { \ 12365816025SJerome Brunet .stream_name = xname " " xsuffix, \ 12465816025SJerome Brunet .channels_min = 1, \ 12565816025SJerome Brunet .channels_max = (xchmax), \ 12665816025SJerome Brunet .rate_min = 5512, \ 12765816025SJerome Brunet .rate_max = 192000, \ 12865816025SJerome Brunet .formats = AIU_ACODEC_CTRL_FORMATS, \ 12965816025SJerome Brunet } 13065816025SJerome Brunet 13165816025SJerome Brunet #define AIU_ACODEC_INPUT(xname) { \ 13265816025SJerome Brunet .name = "ACODEC CTRL " xname, \ 13365816025SJerome Brunet .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \ 13465816025SJerome Brunet .ops = &aiu_acodec_ctrl_input_ops, \ 13565816025SJerome Brunet } 13665816025SJerome Brunet 13765816025SJerome Brunet #define AIU_ACODEC_OUTPUT(xname) { \ 13865816025SJerome Brunet .name = "ACODEC CTRL " xname, \ 13965816025SJerome Brunet .capture = AIU_ACODEC_STREAM(xname, "Capture", AIU_ACODEC_OUT_CHMAX), \ 14065816025SJerome Brunet .ops = &aiu_acodec_ctrl_output_ops, \ 14165816025SJerome Brunet } 14265816025SJerome Brunet 14365816025SJerome Brunet static struct snd_soc_dai_driver aiu_acodec_ctrl_dai_drv[] = { 14465816025SJerome Brunet [CTRL_I2S] = AIU_ACODEC_INPUT("ACODEC I2S IN"), 14565816025SJerome Brunet [CTRL_PCM] = AIU_ACODEC_INPUT("ACODEC PCM IN"), 14665816025SJerome Brunet [CTRL_OUT] = AIU_ACODEC_OUTPUT("ACODEC OUT"), 14765816025SJerome Brunet }; 14865816025SJerome Brunet 14965816025SJerome Brunet static const struct snd_soc_dapm_route aiu_acodec_ctrl_routes[] = { 15065816025SJerome Brunet { "ACODEC SRC", "I2S", "ACODEC I2S IN Playback" }, 15165816025SJerome Brunet { "ACODEC SRC", "PCM", "ACODEC PCM IN Playback" }, 15265816025SJerome Brunet { "ACODEC OUT EN", "Switch", "ACODEC SRC" }, 15365816025SJerome Brunet { "ACODEC OUT Capture", NULL, "ACODEC OUT EN" }, 15465816025SJerome Brunet }; 15565816025SJerome Brunet 15665816025SJerome Brunet static const struct snd_kcontrol_new aiu_acodec_ctrl_controls[] = { 15765816025SJerome Brunet SOC_SINGLE("ACODEC I2S Lane Select", AIU_ACODEC_CTRL, 15865816025SJerome Brunet CTRL_I2S_OUT_LANE_SRC, 3, 0), 15965816025SJerome Brunet }; 16065816025SJerome Brunet 16165816025SJerome Brunet static int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component, 162933f98beSKrzysztof Kozlowski const struct of_phandle_args *args, 16365816025SJerome Brunet const char **dai_name) 16465816025SJerome Brunet { 16565816025SJerome Brunet return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC); 16665816025SJerome Brunet } 16765816025SJerome Brunet 16865816025SJerome Brunet static int aiu_acodec_ctrl_component_probe(struct snd_soc_component *component) 16965816025SJerome Brunet { 17065816025SJerome Brunet /* 17165816025SJerome Brunet * NOTE: Din Skew setting 17265816025SJerome Brunet * According to the documentation, the following update adds one delay 17365816025SJerome Brunet * to the din line. Without this, the output saturates. This happens 17465816025SJerome Brunet * regardless of the link format (i2s or left_j) so it is not clear what 17565816025SJerome Brunet * it actually does but it seems to be required 17665816025SJerome Brunet */ 17765816025SJerome Brunet snd_soc_component_update_bits(component, AIU_ACODEC_CTRL, 17865816025SJerome Brunet CTRL_DIN_SKEW, 17965816025SJerome Brunet FIELD_PREP(CTRL_DIN_SKEW, 2)); 18065816025SJerome Brunet 18165816025SJerome Brunet return 0; 18265816025SJerome Brunet } 18365816025SJerome Brunet 18465816025SJerome Brunet static const struct snd_soc_component_driver aiu_acodec_ctrl_component = { 18565816025SJerome Brunet .name = "AIU Internal DAC Codec Control", 18665816025SJerome Brunet .probe = aiu_acodec_ctrl_component_probe, 18765816025SJerome Brunet .controls = aiu_acodec_ctrl_controls, 18865816025SJerome Brunet .num_controls = ARRAY_SIZE(aiu_acodec_ctrl_controls), 18965816025SJerome Brunet .dapm_widgets = aiu_acodec_ctrl_widgets, 19065816025SJerome Brunet .num_dapm_widgets = ARRAY_SIZE(aiu_acodec_ctrl_widgets), 19165816025SJerome Brunet .dapm_routes = aiu_acodec_ctrl_routes, 19265816025SJerome Brunet .num_dapm_routes = ARRAY_SIZE(aiu_acodec_ctrl_routes), 19365816025SJerome Brunet .of_xlate_dai_name = aiu_acodec_of_xlate_dai_name, 19465816025SJerome Brunet .endianness = 1, 195fc35880dSHeiner Kallweit #ifdef CONFIG_DEBUG_FS 196fc35880dSHeiner Kallweit .debugfs_prefix = "acodec", 197fc35880dSHeiner Kallweit #endif 19865816025SJerome Brunet }; 19965816025SJerome Brunet 20065816025SJerome Brunet int aiu_acodec_ctrl_register_component(struct device *dev) 20165816025SJerome Brunet { 20202471422SJerome Brunet return snd_soc_register_component(dev, &aiu_acodec_ctrl_component, 20365816025SJerome Brunet aiu_acodec_ctrl_dai_drv, 20402471422SJerome Brunet ARRAY_SIZE(aiu_acodec_ctrl_dai_drv)); 20565816025SJerome Brunet } 206