Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/meson/aiu-acodec-ctrl.c
26436 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// Copyright (c) 2020 BayLibre, SAS.
4
// Author: Jerome Brunet <[email protected]>
5
6
#include <linux/bitfield.h>
7
#include <sound/pcm_params.h>
8
#include <sound/soc.h>
9
#include <sound/soc-dai.h>
10
11
#include <dt-bindings/sound/meson-aiu.h>
12
#include "aiu.h"
13
#include "meson-codec-glue.h"
14
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
24
25
#define AIU_ACODEC_OUT_CHMAX 2
26
27
static const char * const aiu_acodec_ctrl_mux_texts[] = {
28
"DISABLED", "I2S", "PCM",
29
};
30
31
static int aiu_acodec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
32
struct snd_ctl_elem_value *ucontrol)
33
{
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;
40
41
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
42
changed = snd_soc_component_test_bits(component, e->reg,
43
CTRL_DIN_LRCLK_SRC,
44
FIELD_PREP(CTRL_DIN_LRCLK_SRC,
45
mux));
46
47
if (!changed)
48
return 0;
49
50
/* Force disconnect of the mux while updating */
51
snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
52
53
snd_soc_component_update_bits(component, e->reg,
54
CTRL_DIN_LRCLK_SRC |
55
CTRL_BCLK_MCLK_SRC,
56
FIELD_PREP(CTRL_DIN_LRCLK_SRC, mux) |
57
FIELD_PREP(CTRL_BCLK_MCLK_SRC, mux));
58
59
snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
60
61
return 1;
62
}
63
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);
67
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);
72
73
static const struct snd_kcontrol_new aiu_acodec_ctrl_out_enable =
74
SOC_DAPM_SINGLE_AUTODISABLE("Switch", AIU_ACODEC_CTRL,
75
CTRL_DIN_EN, 1, 0);
76
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),
82
};
83
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)
87
{
88
struct meson_codec_glue_input *data;
89
int ret;
90
91
ret = meson_codec_glue_input_hw_params(substream, params, dai);
92
if (ret)
93
return ret;
94
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);
101
102
return 0;
103
}
104
105
static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = {
106
.probe = meson_codec_glue_input_dai_probe,
107
.remove = meson_codec_glue_input_dai_remove,
108
.hw_params = aiu_acodec_ctrl_input_hw_params,
109
.set_fmt = meson_codec_glue_input_set_fmt,
110
};
111
112
static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = {
113
.startup = meson_codec_glue_output_startup,
114
};
115
116
#define AIU_ACODEC_CTRL_FORMATS \
117
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
118
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
119
SNDRV_PCM_FMTBIT_S32_LE)
120
121
#define AIU_ACODEC_STREAM(xname, xsuffix, xchmax) \
122
{ \
123
.stream_name = xname " " xsuffix, \
124
.channels_min = 1, \
125
.channels_max = (xchmax), \
126
.rate_min = 5512, \
127
.rate_max = 192000, \
128
.formats = AIU_ACODEC_CTRL_FORMATS, \
129
}
130
131
#define AIU_ACODEC_INPUT(xname) { \
132
.name = "ACODEC CTRL " xname, \
133
.playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \
134
.ops = &aiu_acodec_ctrl_input_ops, \
135
}
136
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, \
141
}
142
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"),
147
};
148
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" },
154
};
155
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),
159
};
160
161
static int aiu_acodec_of_xlate_dai_name(struct snd_soc_component *component,
162
const struct of_phandle_args *args,
163
const char **dai_name)
164
{
165
return aiu_of_xlate_dai_name(component, args, dai_name, AIU_ACODEC);
166
}
167
168
static int aiu_acodec_ctrl_component_probe(struct snd_soc_component *component)
169
{
170
/*
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
176
*/
177
snd_soc_component_update_bits(component, AIU_ACODEC_CTRL,
178
CTRL_DIN_SKEW,
179
FIELD_PREP(CTRL_DIN_SKEW, 2));
180
181
return 0;
182
}
183
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,
194
.endianness = 1,
195
#ifdef CONFIG_DEBUG_FS
196
.debugfs_prefix = "acodec",
197
#endif
198
};
199
200
int aiu_acodec_ctrl_register_component(struct device *dev)
201
{
202
return snd_soc_register_component(dev, &aiu_acodec_ctrl_component,
203
aiu_acodec_ctrl_dai_drv,
204
ARRAY_SIZE(aiu_acodec_ctrl_dai_drv));
205
}
206
207