1 // SPDX-License-Identifier: GPL-2.0
3 // Copyright (c) 2020 BayLibre, SAS.
4 // Author: Jerome Brunet <jbrunet@baylibre.com>
6 #include <linux/bitfield.h>
7 #include <sound/pcm_params.h>
9 #include <sound/soc-dai.h>
11 #include <dt-bindings/sound/meson-aiu.h>
13 #include "meson-codec-glue.h"
15 #define CTRL_DIN_EN 15
16 #define CTRL_CLK_INV BIT(14)
17 #define CTRL_LRCLK_INV BIT(13)
18 #define CTRL_I2S_IN_BCLK_SRC BIT(11)
19 #define CTRL_DIN_LRCLK_SRC_SHIFT 6
20 #define CTRL_DIN_LRCLK_SRC (0x3 << CTRL_DIN_LRCLK_SRC_SHIFT)
21 #define CTRL_BCLK_MCLK_SRC GENMASK(5, 4)
22 #define CTRL_DIN_SKEW GENMASK(3, 2)
23 #define CTRL_I2S_OUT_LANE_SRC 0
25 #define AIU_ACODEC_OUT_CHMAX 2
27 static const char * const aiu_acodec_ctrl_mux_texts
[] = {
28 "DISABLED", "I2S", "PCM",
31 static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol
*kcontrol
,
32 struct snd_ctl_elem_value
*ucontrol
)
34 struct snd_soc_component
*component
=
35 snd_soc_dapm_kcontrol_component(kcontrol
);
36 struct snd_soc_dapm_context
*dapm
=
37 snd_soc_dapm_kcontrol_dapm(kcontrol
);
38 struct soc_enum
*e
= (struct soc_enum
*)kcontrol
->private_value
;
39 unsigned int mux
, changed
;
41 mux
= snd_soc_enum_item_to_val(e
, ucontrol
->value
.enumerated
.item
[0]);
42 changed
= snd_soc_component_test_bits(component
, e
->reg
,
44 FIELD_PREP(CTRL_DIN_LRCLK_SRC
,
50 /* Force disconnect of the mux while updating */
51 snd_soc_dapm_mux_update_power(dapm
, kcontrol
, 0, NULL
, NULL
);
53 snd_soc_component_update_bits(component
, e
->reg
,
56 FIELD_PREP(CTRL_DIN_LRCLK_SRC
, mux
) |
57 FIELD_PREP(CTRL_BCLK_MCLK_SRC
, mux
));
59 snd_soc_dapm_mux_update_power(dapm
, kcontrol
, mux
, e
, NULL
);
64 static SOC_ENUM_SINGLE_DECL(aiu_acodec_ctrl_mux_enum
, AIU_ACODEC_CTRL
,
65 CTRL_DIN_LRCLK_SRC_SHIFT
,
66 aiu_acodec_ctrl_mux_texts
);
68 static const struct snd_kcontrol_new aiu_acodec_ctrl_mux
=
69 SOC_DAPM_ENUM_EXT("ACodec Source", aiu_acodec_ctrl_mux_enum
,
70 snd_soc_dapm_get_enum_double
,
71 aiu_acodec_ctrl_mux_put_enum
);
73 static const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable
=
74 SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL
,
77 static const struct snd_soc_dapm_widget aiu_acodec_ctrl_widgets
[] = {
78 SND_SOC_DAPM_MUX("ACODEC SRC", SND_SOC_NOPM
, 0, 0,
79 &aiu_acodec_ctrl_mux
),
80 SND_SOC_DAPM_SWITCH("ACODEC OUT EN", SND_SOC_NOPM
, 0, 0,
81 &aiu_acodec_ctrl_out_enable
),
84 static int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream
*substream
,
85 struct snd_pcm_hw_params
*params
,
86 struct snd_soc_dai
*dai
)
88 struct meson_codec_glue_input
*data
;
91 ret
= meson_codec_glue_input_hw_params(substream
, params
, dai
);
95 /* The glue will provide 1 lane out of the 4 to the output */
96 data
= meson_codec_glue_input_get_data(dai
);
97 data
->params
.channels_min
= min_t(unsigned int, AIU_ACODEC_OUT_CHMAX
,
98 data
->params
.channels_min
);
99 data
->params
.channels_max
= min_t(unsigned int, AIU_ACODEC_OUT_CHMAX
,
100 data
->params
.channels_max
);
105 static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops
= {
106 .hw_params
= aiu_acodec_ctrl_input_hw_params
,
107 .set_fmt
= meson_codec_glue_input_set_fmt
,
110 static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops
= {
111 .startup
= meson_codec_glue_output_startup
,
114 #define AIU_ACODEC_CTRL_FORMATS \
115 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
116 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
117 SNDRV_PCM_FMTBIT_S32_LE)
119 #define AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \
121 .stream_name = xname " " xsuffix, \
123 .channels_max = (xchmax), \
125 .rate_max = 192000, \
126 .formats = AIU_ACODEC_CTRL_FORMATS, \
129 #define AIU_ACODEC_INPUT(xname) { \
130 .name = "ACODEC CTRL " xname, \
131 .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \
132 .ops = &aiu_acodec_ctrl_input_ops, \
133 .probe = meson_codec_glue_input_dai_probe, \
134 .remove = meson_codec_glue_input_dai_remove, \
137 #define AIU_ACODEC_OUTPUT(xname) { \
138 .name = "ACODEC CTRL " xname, \
139 .capture = AIU_ACODEC_STREAM(xname, "Capture", AIU_ACODEC_OUT_CHMAX), \
140 .ops = &aiu_acodec_ctrl_output_ops, \
143 static struct snd_soc_dai_driver aiu_acodec_ctrl_dai_drv
[] = {
144 [CTRL_I2S
] = AIU_ACODEC_INPUT("ACODEC I2S IN"),
145 [CTRL_PCM
] = AIU_ACODEC_INPUT("ACODEC PCM IN"),
146 [CTRL_OUT
] = AIU_ACODEC_OUTPUT("ACODEC OUT"),
149 static const struct snd_soc_dapm_route aiu_acodec_ctrl_routes
[] = {
150 { "ACODEC SRC", "I2S", "ACODEC I2S IN Playback" },
151 { "ACODEC SRC", "PCM", "ACODEC PCM IN Playback" },
152 { "ACODEC OUT EN", "Switch", "ACODEC SRC" },
153 { "ACODEC OUT Capture", NULL
, "ACODEC OUT EN" },
156 static const struct snd_kcontrol_new aiu_acodec_ctrl_controls
[] = {
157 SOC_SINGLE("ACODEC I2S Lane Select", AIU_ACODEC_CTRL
,
158 CTRL_I2S_OUT_LANE_SRC
, 3, 0),
161 static int aiu_acodec_of_xlate_dai_name(struct snd_soc_component
*component
,
162 struct of_phandle_args
*args
,
163 const char **dai_name
)
165 return aiu_of_xlate_dai_name(component
, args
, dai_name
, AIU_ACODEC
);
168 static int aiu_acodec_ctrl_component_probe(struct snd_soc_component
*component
)
171 * NOTE: Din Skew setting
172 * According to the documentation, the following update adds one delay
173 * to the din line. Without this, the output saturates. This happens
174 * regardless of the link format (i2s or left_j) so it is not clear what
175 * it actually does but it seems to be required
177 snd_soc_component_update_bits(component
, AIU_ACODEC_CTRL
,
179 FIELD_PREP(CTRL_DIN_SKEW
, 2));
184 static const struct snd_soc_component_driver aiu_acodec_ctrl_component
= {
185 .name
= "AIU Internal DAC Codec Control",
186 .probe
= aiu_acodec_ctrl_component_probe
,
187 .controls
= aiu_acodec_ctrl_controls
,
188 .num_controls
= ARRAY_SIZE(aiu_acodec_ctrl_controls
),
189 .dapm_widgets
= aiu_acodec_ctrl_widgets
,
190 .num_dapm_widgets
= ARRAY_SIZE(aiu_acodec_ctrl_widgets
),
191 .dapm_routes
= aiu_acodec_ctrl_routes
,
192 .num_dapm_routes
= ARRAY_SIZE(aiu_acodec_ctrl_routes
),
193 .of_xlate_dai_name
= aiu_acodec_of_xlate_dai_name
,
195 .non_legacy_dai_naming
= 1,
198 int aiu_acodec_ctrl_register_component(struct device
*dev
)
200 return snd_soc_register_component(dev
, &aiu_acodec_ctrl_component
,
201 aiu_acodec_ctrl_dai_drv
,
202 ARRAY_SIZE(aiu_acodec_ctrl_dai_drv
));