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_CLK_SEL GENMASK(1, 0) 1662306a36Sopenharmony_ci#define CTRL_DATA_SEL_SHIFT 4 1762306a36Sopenharmony_ci#define CTRL_DATA_SEL (0x3 << CTRL_DATA_SEL_SHIFT) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const char * const aiu_codec_ctrl_mux_texts[] = { 2062306a36Sopenharmony_ci "DISABLED", "PCM", "I2S", 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, 2462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct snd_soc_component *component = 2762306a36Sopenharmony_ci snd_soc_dapm_kcontrol_component(kcontrol); 2862306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 2962306a36Sopenharmony_ci snd_soc_dapm_kcontrol_dapm(kcontrol); 3062306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 3162306a36Sopenharmony_ci unsigned int mux, changed; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); 3462306a36Sopenharmony_ci changed = snd_soc_component_test_bits(component, e->reg, 3562306a36Sopenharmony_ci CTRL_DATA_SEL, 3662306a36Sopenharmony_ci FIELD_PREP(CTRL_DATA_SEL, mux)); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (!changed) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* Force disconnect of the mux while updating */ 4262306a36Sopenharmony_ci snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* Reset the source first */ 4562306a36Sopenharmony_ci snd_soc_component_update_bits(component, e->reg, 4662306a36Sopenharmony_ci CTRL_CLK_SEL | 4762306a36Sopenharmony_ci CTRL_DATA_SEL, 4862306a36Sopenharmony_ci FIELD_PREP(CTRL_CLK_SEL, 0) | 4962306a36Sopenharmony_ci FIELD_PREP(CTRL_DATA_SEL, 0)); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Set the appropriate source */ 5262306a36Sopenharmony_ci snd_soc_component_update_bits(component, e->reg, 5362306a36Sopenharmony_ci CTRL_CLK_SEL | 5462306a36Sopenharmony_ci CTRL_DATA_SEL, 5562306a36Sopenharmony_ci FIELD_PREP(CTRL_CLK_SEL, mux) | 5662306a36Sopenharmony_ci FIELD_PREP(CTRL_DATA_SEL, mux)); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 1; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aiu_hdmi_ctrl_mux_enum, AIU_HDMI_CLK_DATA_CTRL, 6462306a36Sopenharmony_ci CTRL_DATA_SEL_SHIFT, 6562306a36Sopenharmony_ci aiu_codec_ctrl_mux_texts); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic const struct snd_kcontrol_new aiu_hdmi_ctrl_mux = 6862306a36Sopenharmony_ci SOC_DAPM_ENUM_EXT("HDMI Source", aiu_hdmi_ctrl_mux_enum, 6962306a36Sopenharmony_ci snd_soc_dapm_get_enum_double, 7062306a36Sopenharmony_ci aiu_codec_ctrl_mux_put_enum); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = { 7362306a36Sopenharmony_ci SND_SOC_DAPM_MUX("HDMI CTRL SRC", SND_SOC_NOPM, 0, 0, 7462306a36Sopenharmony_ci &aiu_hdmi_ctrl_mux), 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = { 7862306a36Sopenharmony_ci .probe = meson_codec_glue_input_dai_probe, 7962306a36Sopenharmony_ci .remove = meson_codec_glue_input_dai_remove, 8062306a36Sopenharmony_ci .hw_params = meson_codec_glue_input_hw_params, 8162306a36Sopenharmony_ci .set_fmt = meson_codec_glue_input_set_fmt, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = { 8562306a36Sopenharmony_ci .startup = meson_codec_glue_output_startup, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define AIU_CODEC_CTRL_FORMATS \ 8962306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 9062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ 9162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define AIU_CODEC_CTRL_STREAM(xname, xsuffix) \ 9462306a36Sopenharmony_ci{ \ 9562306a36Sopenharmony_ci .stream_name = xname " " xsuffix, \ 9662306a36Sopenharmony_ci .channels_min = 1, \ 9762306a36Sopenharmony_ci .channels_max = 8, \ 9862306a36Sopenharmony_ci .rate_min = 5512, \ 9962306a36Sopenharmony_ci .rate_max = 192000, \ 10062306a36Sopenharmony_ci .formats = AIU_CODEC_CTRL_FORMATS, \ 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define AIU_CODEC_CTRL_INPUT(xname) { \ 10462306a36Sopenharmony_ci .name = "CODEC CTRL " xname, \ 10562306a36Sopenharmony_ci .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"), \ 10662306a36Sopenharmony_ci .ops = &aiu_codec_ctrl_input_ops, \ 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define AIU_CODEC_CTRL_OUTPUT(xname) { \ 11062306a36Sopenharmony_ci .name = "CODEC CTRL " xname, \ 11162306a36Sopenharmony_ci .capture = AIU_CODEC_CTRL_STREAM(xname, "Capture"), \ 11262306a36Sopenharmony_ci .ops = &aiu_codec_ctrl_output_ops, \ 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct snd_soc_dai_driver aiu_hdmi_ctrl_dai_drv[] = { 11662306a36Sopenharmony_ci [CTRL_I2S] = AIU_CODEC_CTRL_INPUT("HDMI I2S IN"), 11762306a36Sopenharmony_ci [CTRL_PCM] = AIU_CODEC_CTRL_INPUT("HDMI PCM IN"), 11862306a36Sopenharmony_ci [CTRL_OUT] = AIU_CODEC_CTRL_OUTPUT("HDMI OUT"), 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic const struct snd_soc_dapm_route aiu_hdmi_ctrl_routes[] = { 12262306a36Sopenharmony_ci { "HDMI CTRL SRC", "I2S", "HDMI I2S IN Playback" }, 12362306a36Sopenharmony_ci { "HDMI CTRL SRC", "PCM", "HDMI PCM IN Playback" }, 12462306a36Sopenharmony_ci { "HDMI OUT Capture", NULL, "HDMI CTRL SRC" }, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int aiu_hdmi_of_xlate_dai_name(struct snd_soc_component *component, 12862306a36Sopenharmony_ci const struct of_phandle_args *args, 12962306a36Sopenharmony_ci const char **dai_name) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return aiu_of_xlate_dai_name(component, args, dai_name, AIU_HDMI); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic const struct snd_soc_component_driver aiu_hdmi_ctrl_component = { 13562306a36Sopenharmony_ci .name = "AIU HDMI Codec Control", 13662306a36Sopenharmony_ci .dapm_widgets = aiu_hdmi_ctrl_widgets, 13762306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(aiu_hdmi_ctrl_widgets), 13862306a36Sopenharmony_ci .dapm_routes = aiu_hdmi_ctrl_routes, 13962306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(aiu_hdmi_ctrl_routes), 14062306a36Sopenharmony_ci .of_xlate_dai_name = aiu_hdmi_of_xlate_dai_name, 14162306a36Sopenharmony_ci .endianness = 1, 14262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 14362306a36Sopenharmony_ci .debugfs_prefix = "hdmi", 14462306a36Sopenharmony_ci#endif 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint aiu_hdmi_ctrl_register_component(struct device *dev) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return snd_soc_register_component(dev, &aiu_hdmi_ctrl_component, 15062306a36Sopenharmony_ci aiu_hdmi_ctrl_dai_drv, 15162306a36Sopenharmony_ci ARRAY_SIZE(aiu_hdmi_ctrl_dai_drv)); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 154