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