162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2020 BayLibre, SAS. 462306a36Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <sound/pcm_params.h> 862306a36Sopenharmony_ci#include <sound/soc.h> 962306a36Sopenharmony_ci#include <sound/soc-dai.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <dt-bindings/sound/meson-aiu.h> 1262306a36Sopenharmony_ci#include "aiu.h" 1362306a36Sopenharmony_ci#include "meson-codec-glue.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define CTRL_DIN_EN 15 1662306a36Sopenharmony_ci#define CTRL_CLK_INV BIT(14) 1762306a36Sopenharmony_ci#define CTRL_LRCLK_INV BIT(13) 1862306a36Sopenharmony_ci#define CTRL_I2S_IN_BCLK_SRC BIT(11) 1962306a36Sopenharmony_ci#define CTRL_DIN_LRCLK_SRC_SHIFT 6 2062306a36Sopenharmony_ci#define CTRL_DIN_LRCLK_SRC (0x3 << CTRL_DIN_LRCLK_SRC_SHIFT) 2162306a36Sopenharmony_ci#define CTRL_BCLK_MCLK_SRC GENMASK(5, 4) 2262306a36Sopenharmony_ci#define CTRL_DIN_SKEW GENMASK(3, 2) 2362306a36Sopenharmony_ci#define CTRL_I2S_OUT_LANE_SRC 0 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define AIU_ACODEC_OUT_CHMAX 2 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const char * const aiu_acodec_ctrl_mux_texts[] = { 2862306a36Sopenharmony_ci "DISABLED", "I2S", "PCM", 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, 3262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct snd_soc_component *component = 3562306a36Sopenharmony_ci snd_soc_dapm_kcontrol_component(kcontrol); 3662306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 3762306a36Sopenharmony_ci snd_soc_dapm_kcontrol_dapm(kcontrol); 3862306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 3962306a36Sopenharmony_ci unsigned int mux, changed; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); 4262306a36Sopenharmony_ci changed = snd_soc_component_test_bits(component, e->reg, 4362306a36Sopenharmony_ci CTRL_DIN_LRCLK_SRC, 4462306a36Sopenharmony_ci FIELD_PREP(CTRL_DIN_LRCLK_SRC, 4562306a36Sopenharmony_ci mux)); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (!changed) 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* Force disconnect of the mux while updating */ 5162306a36Sopenharmony_ci snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci snd_soc_component_update_bits(component, e->reg, 5462306a36Sopenharmony_ci CTRL_DIN_LRCLK_SRC | 5562306a36Sopenharmony_ci CTRL_BCLK_MCLK_SRC, 5662306a36Sopenharmony_ci FIELD_PREP(CTRL_DIN_LRCLK_SRC, mux) | 5762306a36Sopenharmony_ci FIELD_PREP(CTRL_BCLK_MCLK_SRC, mux)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 1; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aiu_acodec_ctrl_mux_enum, AIU_ACODEC_CTRL, 6562306a36Sopenharmony_ci CTRL_DIN_LRCLK_SRC_SHIFT, 6662306a36Sopenharmony_ci aiu_acodec_ctrl_mux_texts); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const struct snd_kcontrol_new aiu_acodec_ctrl_mux = 6962306a36Sopenharmony_ci SOC_DAPM_ENUM_EXT("ACodec Source", aiu_acodec_ctrl_mux_enum, 7062306a36Sopenharmony_ci snd_soc_dapm_get_enum_double, 7162306a36Sopenharmony_ci aiu_acodec_ctrl_mux_put_enum); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable = 7462306a36Sopenharmony_ci SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL, 7562306a36Sopenharmony_ci CTRL_DIN_EN, 1, 0); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget aiu_acodec_ctrl_widgets[] = { 7862306a36Sopenharmony_ci SND_SOC_DAPM_MUX("ACODEC SRC", SND_SOC_NOPM, 0, 0, 7962306a36Sopenharmony_ci &aiu_acodec_ctrl_mux), 8062306a36Sopenharmony_ci SND_SOC_DAPM_SWITCH("ACODEC OUT EN", SND_SOC_NOPM, 0, 0, 8162306a36Sopenharmony_ci &aiu_acodec_ctrl_out_enable), 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream, 8562306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 8662306a36Sopenharmony_ci struct snd_soc_dai *dai) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct meson_codec_glue_input *data; 8962306a36Sopenharmony_ci int ret; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret = meson_codec_glue_input_hw_params(substream, params, dai); 9262306a36Sopenharmony_ci if (ret) 9362306a36Sopenharmony_ci return ret; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* The glue will provide 1 lane out of the 4 to the output */ 9662306a36Sopenharmony_ci data = meson_codec_glue_input_get_data(dai); 9762306a36Sopenharmony_ci data->params.channels_min = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, 9862306a36Sopenharmony_ci data->params.channels_min); 9962306a36Sopenharmony_ci data->params.channels_max = min_t(unsigned int, AIU_ACODEC_OUT_CHMAX, 10062306a36Sopenharmony_ci data->params.channels_max); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = { 10662306a36Sopenharmony_ci .probe = meson_codec_glue_input_dai_probe, 10762306a36Sopenharmony_ci .remove = meson_codec_glue_input_dai_remove, 10862306a36Sopenharmony_ci .hw_params = aiu_acodec_ctrl_input_hw_params, 10962306a36Sopenharmony_ci .set_fmt = meson_codec_glue_input_set_fmt, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = { 11362306a36Sopenharmony_ci .startup = meson_codec_glue_output_startup, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define AIU_ACODEC_CTRL_FORMATS \ 11762306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 11862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ 11962306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \ 12262306a36Sopenharmony_ci{ \ 12362306a36Sopenharmony_ci .stream_name = xname " " xsuffix, \ 12462306a36Sopenharmony_ci .channels_min = 1, \ 12562306a36Sopenharmony_ci .channels_max = (xchmax), \ 12662306a36Sopenharmony_ci .rate_min = 5512, \ 12762306a36Sopenharmony_ci .rate_max = 192000, \ 12862306a36Sopenharmony_ci .formats = AIU_ACODEC_CTRL_FORMATS, \ 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define AIU_ACODEC_INPUT(xname) { \ 13262306a36Sopenharmony_ci .name = "ACODEC CTRL " xname, \ 13362306a36Sopenharmony_ci .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \ 13462306a36Sopenharmony_ci .ops = &aiu_acodec_ctrl_input_ops, \ 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define AIU_ACODEC_OUTPUT(xname) { \ 13862306a36Sopenharmony_ci .name = "ACODEC CTRL " xname, \ 13962306a36Sopenharmony_ci .capture = AIU_ACODEC_STREAM(xname, "Capture", AIU_ACODEC_OUT_CHMAX), \ 14062306a36Sopenharmony_ci .ops = &aiu_acodec_ctrl_output_ops, \ 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct snd_soc_dai_driver aiu_acodec_ctrl_dai_drv[] = { 14462306a36Sopenharmony_ci [CTRL_I2S] = AIU_ACODEC_INPUT("ACODEC I2S IN"), 14562306a36Sopenharmony_ci [CTRL_PCM] = AIU_ACODEC_INPUT("ACODEC PCM IN"), 14662306a36Sopenharmony_ci [CTRL_OUT] = AIU_ACODEC_OUTPUT("ACODEC OUT"), 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const struct snd_soc_dapm_route aiu_acodec_ctrl_routes[] = { 15062306a36Sopenharmony_ci { "ACODEC SRC", "I2S", "ACODEC I2S IN Playback" }, 15162306a36Sopenharmony_ci { "ACODEC SRC", "PCM", "ACODEC PCM IN Playback" }, 15262306a36Sopenharmony_ci { "ACODEC OUT EN", "Switch", "ACODEC SRC" }, 15362306a36Sopenharmony_ci { "ACODEC OUT Capture", NULL, "ACODEC OUT EN" }, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct snd_kcontrol_new aiu_acodec_ctrl_controls[] = { 15762306a36Sopenharmony_ci SOC_SINGLE("ACODEC I2S Lane Select", AIU_ACODEC_CTRL, 15862306a36Sopenharmony_ci CTRL_I2S_OUT_LANE_SRC, 3, 0), 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component, 16262306a36Sopenharmony_ci const struct of_phandle_args *args, 16362306a36Sopenharmony_ci const char **dai_name) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int aiu_acodec_ctrl_component_probe(struct snd_soc_component *component) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * NOTE: Din Skew setting 17262306a36Sopenharmony_ci * According to the documentation, the following update adds one delay 17362306a36Sopenharmony_ci * to the din line. Without this, the output saturates. This happens 17462306a36Sopenharmony_ci * regardless of the link format (i2s or left_j) so it is not clear what 17562306a36Sopenharmony_ci * it actually does but it seems to be required 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci snd_soc_component_update_bits(component, AIU_ACODEC_CTRL, 17862306a36Sopenharmony_ci CTRL_DIN_SKEW, 17962306a36Sopenharmony_ci FIELD_PREP(CTRL_DIN_SKEW, 2)); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic const struct snd_soc_component_driver aiu_acodec_ctrl_component = { 18562306a36Sopenharmony_ci .name = "AIU Internal DAC Codec Control", 18662306a36Sopenharmony_ci .probe = aiu_acodec_ctrl_component_probe, 18762306a36Sopenharmony_ci .controls = aiu_acodec_ctrl_controls, 18862306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(aiu_acodec_ctrl_controls), 18962306a36Sopenharmony_ci .dapm_widgets = aiu_acodec_ctrl_widgets, 19062306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(aiu_acodec_ctrl_widgets), 19162306a36Sopenharmony_ci .dapm_routes = aiu_acodec_ctrl_routes, 19262306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(aiu_acodec_ctrl_routes), 19362306a36Sopenharmony_ci .of_xlate_dai_name = aiu_acodec_of_xlate_dai_name, 19462306a36Sopenharmony_ci .endianness = 1, 19562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 19662306a36Sopenharmony_ci .debugfs_prefix = "acodec", 19762306a36Sopenharmony_ci#endif 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciint aiu_acodec_ctrl_register_component(struct device *dev) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci return snd_soc_register_component(dev, &aiu_acodec_ctrl_component, 20362306a36Sopenharmony_ci aiu_acodec_ctrl_dai_drv, 20462306a36Sopenharmony_ci ARRAY_SIZE(aiu_acodec_ctrl_dai_drv)); 20562306a36Sopenharmony_ci} 206