18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2020 BayLibre, SAS. 48c2ecf20Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 78c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 88c2ecf20Sopenharmony_ci#include <sound/soc.h> 98c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <dt-bindings/sound/meson-aiu.h> 128c2ecf20Sopenharmony_ci#include "aiu.h" 138c2ecf20Sopenharmony_ci#include "meson-codec-glue.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define CTRL_DIN_EN 15 168c2ecf20Sopenharmony_ci#define CTRL_CLK_INV BIT(14) 178c2ecf20Sopenharmony_ci#define CTRL_LRCLK_INV BIT(13) 188c2ecf20Sopenharmony_ci#define CTRL_I2S_IN_BCLK_SRC BIT(11) 198c2ecf20Sopenharmony_ci#define CTRL_DIN_LRCLK_SRC_SHIFT 6 208c2ecf20Sopenharmony_ci#define CTRL_DIN_LRCLK_SRC (0x3 << CTRL_DIN_LRCLK_SRC_SHIFT) 218c2ecf20Sopenharmony_ci#define CTRL_BCLK_MCLK_SRC GENMASK(5, 4) 228c2ecf20Sopenharmony_ci#define CTRL_DIN_SKEW GENMASK(3, 2) 238c2ecf20Sopenharmony_ci#define CTRL_I2S_OUT_LANE_SRC 0 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define AIU_ACODEC_OUT_CHMAX 2 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const char * const aiu_acodec_ctrl_mux_texts[] = { 288c2ecf20Sopenharmony_ci "DISABLED", "I2S", "PCM", 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, 328c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct snd_soc_component *component = 358c2ecf20Sopenharmony_ci snd_soc_dapm_kcontrol_component(kcontrol); 368c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = 378c2ecf20Sopenharmony_ci snd_soc_dapm_kcontrol_dapm(kcontrol); 388c2ecf20Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 398c2ecf20Sopenharmony_ci unsigned int mux, changed; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); 428c2ecf20Sopenharmony_ci changed = snd_soc_component_test_bits(component, e->reg, 438c2ecf20Sopenharmony_ci CTRL_DIN_LRCLK_SRC, 448c2ecf20Sopenharmony_ci FIELD_PREP(CTRL_DIN_LRCLK_SRC, 458c2ecf20Sopenharmony_ci mux)); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!changed) 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Force disconnect of the mux while updating */ 518c2ecf20Sopenharmony_ci snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, e->reg, 548c2ecf20Sopenharmony_ci CTRL_DIN_LRCLK_SRC | 558c2ecf20Sopenharmony_ci CTRL_BCLK_MCLK_SRC, 568c2ecf20Sopenharmony_ci FIELD_PREP(CTRL_DIN_LRCLK_SRC, mux) | 578c2ecf20Sopenharmony_ci FIELD_PREP(CTRL_BCLK_MCLK_SRC, mux)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return 1; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aiu_acodec_ctrl_mux_enum, AIU_ACODEC_CTRL, 658c2ecf20Sopenharmony_ci CTRL_DIN_LRCLK_SRC_SHIFT, 668c2ecf20Sopenharmony_ci aiu_acodec_ctrl_mux_texts); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new aiu_acodec_ctrl_mux = 698c2ecf20Sopenharmony_ci SOC_DAPM_ENUM_EXT("ACodec Source", aiu_acodec_ctrl_mux_enum, 708c2ecf20Sopenharmony_ci snd_soc_dapm_get_enum_double, 718c2ecf20Sopenharmony_ci aiu_acodec_ctrl_mux_put_enum); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable = 748c2ecf20Sopenharmony_ci SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL, 758c2ecf20Sopenharmony_ci CTRL_DIN_EN, 1, 0); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget aiu_acodec_ctrl_widgets[] = { 788c2ecf20Sopenharmony_ci SND_SOC_DAPM_MUX("ACODEC SRC", SND_SOC_NOPM, 0, 0, 798c2ecf20Sopenharmony_ci &aiu_acodec_ctrl_mux), 808c2ecf20Sopenharmony_ci SND_SOC_DAPM_SWITCH("ACODEC OUT EN", SND_SOC_NOPM, 0, 0, 818c2ecf20Sopenharmony_ci &aiu_acodec_ctrl_out_enable), 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream, 858c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 868c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct meson_codec_glue_input *data; 898c2ecf20Sopenharmony_ci int ret; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ret = meson_codec_glue_input_hw_params(substream, params, dai); 928c2ecf20Sopenharmony_ci if (ret) 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* The glue will provide 1 lane out of the 4 to the output */ 968c2ecf20Sopenharmony_ci data = meson_codec_glue_input_get_data(dai); 978c2ecf20Sopenharmony_ci data->params.channels_min = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, 988c2ecf20Sopenharmony_ci data->params.channels_min); 998c2ecf20Sopenharmony_ci data->params.channels_max = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, 1008c2ecf20Sopenharmony_ci data->params.channels_max); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = { 1068c2ecf20Sopenharmony_ci .hw_params = aiu_acodec_ctrl_input_hw_params, 1078c2ecf20Sopenharmony_ci .set_fmt = meson_codec_glue_input_set_fmt, 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = { 1118c2ecf20Sopenharmony_ci .startup = meson_codec_glue_output_startup, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define AIU_ACODEC_CTRL_FORMATS \ 1158c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 1168c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ 1178c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \ 1208c2ecf20Sopenharmony_ci{ \ 1218c2ecf20Sopenharmony_ci .stream_name = xname " " xsuffix, \ 1228c2ecf20Sopenharmony_ci .channels_min = 1, \ 1238c2ecf20Sopenharmony_ci .channels_max = (xchmax), \ 1248c2ecf20Sopenharmony_ci .rate_min = 5512, \ 1258c2ecf20Sopenharmony_ci .rate_max = 192000, \ 1268c2ecf20Sopenharmony_ci .formats = AIU_ACODEC_CTRL_FORMATS, \ 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define AIU_ACODEC_INPUT(xname) { \ 1308c2ecf20Sopenharmony_ci .name = "ACODEC CTRL " xname, \ 1318c2ecf20Sopenharmony_ci .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \ 1328c2ecf20Sopenharmony_ci .ops = &aiu_acodec_ctrl_input_ops, \ 1338c2ecf20Sopenharmony_ci .probe = meson_codec_glue_input_dai_probe, \ 1348c2ecf20Sopenharmony_ci .remove = meson_codec_glue_input_dai_remove, \ 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define AIU_ACODEC_OUTPUT(xname) { \ 1388c2ecf20Sopenharmony_ci .name = "ACODEC CTRL " xname, \ 1398c2ecf20Sopenharmony_ci .capture = AIU_ACODEC_STREAM(xname, "Capture", AIU_ACODEC_OUT_CHMAX), \ 1408c2ecf20Sopenharmony_ci .ops = &aiu_acodec_ctrl_output_ops, \ 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver aiu_acodec_ctrl_dai_drv[] = { 1448c2ecf20Sopenharmony_ci [CTRL_I2S] = AIU_ACODEC_INPUT("ACODEC I2S IN"), 1458c2ecf20Sopenharmony_ci [CTRL_PCM] = AIU_ACODEC_INPUT("ACODEC PCM IN"), 1468c2ecf20Sopenharmony_ci [CTRL_OUT] = AIU_ACODEC_OUTPUT("ACODEC OUT"), 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route aiu_acodec_ctrl_routes[] = { 1508c2ecf20Sopenharmony_ci { "ACODEC SRC", "I2S", "ACODEC I2S IN Playback" }, 1518c2ecf20Sopenharmony_ci { "ACODEC SRC", "PCM", "ACODEC PCM IN Playback" }, 1528c2ecf20Sopenharmony_ci { "ACODEC OUT EN", "Switch", "ACODEC SRC" }, 1538c2ecf20Sopenharmony_ci { "ACODEC OUT Capture", NULL, "ACODEC OUT EN" }, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new aiu_acodec_ctrl_controls[] = { 1578c2ecf20Sopenharmony_ci SOC_SINGLE("ACODEC I2S Lane Select", AIU_ACODEC_CTRL, 1588c2ecf20Sopenharmony_ci CTRL_I2S_OUT_LANE_SRC, 3, 0), 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component, 1628c2ecf20Sopenharmony_ci struct of_phandle_args *args, 1638c2ecf20Sopenharmony_ci const char **dai_name) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int aiu_acodec_ctrl_component_probe(struct snd_soc_component *component) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * NOTE: Din Skew setting 1728c2ecf20Sopenharmony_ci * According to the documentation, the following update adds one delay 1738c2ecf20Sopenharmony_ci * to the din line. Without this, the output saturates. This happens 1748c2ecf20Sopenharmony_ci * regardless of the link format (i2s or left_j) so it is not clear what 1758c2ecf20Sopenharmony_ci * it actually does but it seems to be required 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, AIU_ACODEC_CTRL, 1788c2ecf20Sopenharmony_ci CTRL_DIN_SKEW, 1798c2ecf20Sopenharmony_ci FIELD_PREP(CTRL_DIN_SKEW, 2)); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver aiu_acodec_ctrl_component = { 1858c2ecf20Sopenharmony_ci .name = "AIU Internal DAC Codec Control", 1868c2ecf20Sopenharmony_ci .probe = aiu_acodec_ctrl_component_probe, 1878c2ecf20Sopenharmony_ci .controls = aiu_acodec_ctrl_controls, 1888c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(aiu_acodec_ctrl_controls), 1898c2ecf20Sopenharmony_ci .dapm_widgets = aiu_acodec_ctrl_widgets, 1908c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(aiu_acodec_ctrl_widgets), 1918c2ecf20Sopenharmony_ci .dapm_routes = aiu_acodec_ctrl_routes, 1928c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(aiu_acodec_ctrl_routes), 1938c2ecf20Sopenharmony_ci .of_xlate_dai_name = aiu_acodec_of_xlate_dai_name, 1948c2ecf20Sopenharmony_ci .endianness = 1, 1958c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint aiu_acodec_ctrl_register_component(struct device *dev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return snd_soc_register_component(dev, &aiu_acodec_ctrl_component, 2018c2ecf20Sopenharmony_ci aiu_acodec_ctrl_dai_drv, 2028c2ecf20Sopenharmony_ci ARRAY_SIZE(aiu_acodec_ctrl_dai_drv)); 2038c2ecf20Sopenharmony_ci} 204