1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // uda1334.c -- UDA1334 ALSA SoC Audio driver |
4 | // |
5 | // Based on WM8523 ALSA SoC Audio driver written by Mark Brown |
6 | |
7 | #include <linux/mod_devicetable.h> |
8 | #include <linux/module.h> |
9 | #include <linux/moduleparam.h> |
10 | #include <linux/init.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/gpio/consumer.h> |
14 | #include <sound/core.h> |
15 | #include <sound/pcm.h> |
16 | #include <sound/pcm_params.h> |
17 | #include <sound/soc.h> |
18 | #include <sound/initval.h> |
19 | |
20 | #define UDA1334_NUM_RATES 6 |
21 | |
22 | /* codec private data */ |
23 | struct uda1334_priv { |
24 | struct gpio_desc *mute; |
25 | struct gpio_desc *deemph; |
26 | unsigned int sysclk; |
27 | unsigned int rate_constraint_list[UDA1334_NUM_RATES]; |
28 | struct snd_pcm_hw_constraint_list rate_constraint; |
29 | }; |
30 | |
31 | static const struct snd_soc_dapm_widget uda1334_dapm_widgets[] = { |
32 | SND_SOC_DAPM_DAC("DAC" , "Playback" , SND_SOC_NOPM, 0, 0), |
33 | SND_SOC_DAPM_OUTPUT("LINEVOUTL" ), |
34 | SND_SOC_DAPM_OUTPUT("LINEVOUTR" ), |
35 | }; |
36 | |
37 | static const struct snd_soc_dapm_route uda1334_dapm_routes[] = { |
38 | { "LINEVOUTL" , NULL, "DAC" }, |
39 | { "LINEVOUTR" , NULL, "DAC" }, |
40 | }; |
41 | |
42 | static int uda1334_put_deemph(struct snd_kcontrol *kcontrol, |
43 | struct snd_ctl_elem_value *ucontrol) |
44 | { |
45 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
46 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: component); |
47 | int deemph = ucontrol->value.integer.value[0]; |
48 | |
49 | if (deemph > 1) |
50 | return -EINVAL; |
51 | |
52 | gpiod_set_value_cansleep(desc: uda1334->deemph, value: deemph); |
53 | |
54 | return 0; |
55 | }; |
56 | |
57 | static int uda1334_get_deemph(struct snd_kcontrol *kcontrol, |
58 | struct snd_ctl_elem_value *ucontrol) |
59 | { |
60 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
61 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: component); |
62 | int ret; |
63 | |
64 | ret = gpiod_get_value_cansleep(desc: uda1334->deemph); |
65 | if (ret < 0) |
66 | return -EINVAL; |
67 | |
68 | ucontrol->value.integer.value[0] = ret; |
69 | |
70 | return 0; |
71 | }; |
72 | |
73 | static const struct snd_kcontrol_new uda1334_snd_controls[] = { |
74 | SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch" , 0, |
75 | uda1334_get_deemph, uda1334_put_deemph), |
76 | }; |
77 | |
78 | static const struct { |
79 | int value; |
80 | int ratio; |
81 | } lrclk_ratios[UDA1334_NUM_RATES] = { |
82 | { 1, 128 }, |
83 | { 2, 192 }, |
84 | { 3, 256 }, |
85 | { 4, 384 }, |
86 | { 5, 512 }, |
87 | { 6, 768 }, |
88 | }; |
89 | |
90 | static int uda1334_startup(struct snd_pcm_substream *substream, |
91 | struct snd_soc_dai *dai) |
92 | { |
93 | struct snd_soc_component *component = dai->component; |
94 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: component); |
95 | |
96 | /* |
97 | * The set of sample rates that can be supported depends on the |
98 | * MCLK supplied to the CODEC - enforce this. |
99 | */ |
100 | if (!uda1334->sysclk) { |
101 | dev_err(component->dev, |
102 | "No MCLK configured, call set_sysclk() on init\n" ); |
103 | return -EINVAL; |
104 | } |
105 | |
106 | snd_pcm_hw_constraint_list(runtime: substream->runtime, cond: 0, |
107 | SNDRV_PCM_HW_PARAM_RATE, |
108 | l: &uda1334->rate_constraint); |
109 | |
110 | gpiod_set_value_cansleep(desc: uda1334->mute, value: 1); |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | static void uda1334_shutdown(struct snd_pcm_substream *substream, |
116 | struct snd_soc_dai *dai) |
117 | { |
118 | struct snd_soc_component *component = dai->component; |
119 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: component); |
120 | |
121 | gpiod_set_value_cansleep(desc: uda1334->mute, value: 0); |
122 | } |
123 | |
124 | static int uda1334_set_dai_sysclk(struct snd_soc_dai *codec_dai, |
125 | int clk_id, unsigned int freq, int dir) |
126 | { |
127 | struct snd_soc_component *component = codec_dai->component; |
128 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: component); |
129 | unsigned int val; |
130 | int i, j = 0; |
131 | |
132 | uda1334->sysclk = freq; |
133 | |
134 | uda1334->rate_constraint.count = 0; |
135 | for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { |
136 | val = freq / lrclk_ratios[i].ratio; |
137 | /* |
138 | * Check that it's a standard rate since core can't |
139 | * cope with others and having the odd rates confuses |
140 | * constraint matching. |
141 | */ |
142 | |
143 | switch (val) { |
144 | case 8000: |
145 | case 32000: |
146 | case 44100: |
147 | case 48000: |
148 | case 64000: |
149 | case 88200: |
150 | case 96000: |
151 | dev_dbg(component->dev, "Supported sample rate: %dHz\n" , |
152 | val); |
153 | uda1334->rate_constraint_list[j++] = val; |
154 | uda1334->rate_constraint.count++; |
155 | break; |
156 | default: |
157 | dev_dbg(component->dev, "Skipping sample rate: %dHz\n" , |
158 | val); |
159 | } |
160 | } |
161 | |
162 | /* Need at least one supported rate... */ |
163 | if (uda1334->rate_constraint.count == 0) |
164 | return -EINVAL; |
165 | |
166 | return 0; |
167 | } |
168 | |
169 | static int uda1334_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) |
170 | { |
171 | fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | |
172 | SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); |
173 | |
174 | if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
175 | SND_SOC_DAIFMT_CBC_CFC)) { |
176 | dev_err(codec_dai->dev, "Invalid DAI format\n" ); |
177 | return -EINVAL; |
178 | } |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | static int uda1334_mute_stream(struct snd_soc_dai *dai, int mute, int stream) |
184 | { |
185 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: dai->component); |
186 | |
187 | if (uda1334->mute) |
188 | gpiod_set_value_cansleep(desc: uda1334->mute, value: mute); |
189 | |
190 | return 0; |
191 | } |
192 | |
193 | #define UDA1334_RATES SNDRV_PCM_RATE_8000_96000 |
194 | |
195 | #define UDA1334_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) |
196 | |
197 | static const struct snd_soc_dai_ops uda1334_dai_ops = { |
198 | .startup = uda1334_startup, |
199 | .shutdown = uda1334_shutdown, |
200 | .set_sysclk = uda1334_set_dai_sysclk, |
201 | .set_fmt = uda1334_set_fmt, |
202 | .mute_stream = uda1334_mute_stream, |
203 | }; |
204 | |
205 | static struct snd_soc_dai_driver uda1334_dai = { |
206 | .name = "uda1334-hifi" , |
207 | .playback = { |
208 | .stream_name = "Playback" , |
209 | .channels_min = 2, |
210 | .channels_max = 2, |
211 | .rates = UDA1334_RATES, |
212 | .formats = UDA1334_FORMATS, |
213 | }, |
214 | .ops = &uda1334_dai_ops, |
215 | }; |
216 | |
217 | static int uda1334_probe(struct snd_soc_component *component) |
218 | { |
219 | struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(c: component); |
220 | |
221 | uda1334->rate_constraint.list = &uda1334->rate_constraint_list[0]; |
222 | uda1334->rate_constraint.count = |
223 | ARRAY_SIZE(uda1334->rate_constraint_list); |
224 | |
225 | return 0; |
226 | } |
227 | |
228 | static const struct snd_soc_component_driver soc_component_dev_uda1334 = { |
229 | .probe = uda1334_probe, |
230 | .controls = uda1334_snd_controls, |
231 | .num_controls = ARRAY_SIZE(uda1334_snd_controls), |
232 | .dapm_widgets = uda1334_dapm_widgets, |
233 | .num_dapm_widgets = ARRAY_SIZE(uda1334_dapm_widgets), |
234 | .dapm_routes = uda1334_dapm_routes, |
235 | .num_dapm_routes = ARRAY_SIZE(uda1334_dapm_routes), |
236 | .idle_bias_on = 1, |
237 | .use_pmdown_time = 1, |
238 | .endianness = 1, |
239 | }; |
240 | |
241 | static const struct of_device_id uda1334_of_match[] = { |
242 | { .compatible = "nxp,uda1334" }, |
243 | { /* sentinel*/ } |
244 | }; |
245 | MODULE_DEVICE_TABLE(of, uda1334_of_match); |
246 | |
247 | static int uda1334_codec_probe(struct platform_device *pdev) |
248 | { |
249 | struct uda1334_priv *uda1334; |
250 | int ret; |
251 | |
252 | uda1334 = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct uda1334_priv), |
253 | GFP_KERNEL); |
254 | if (!uda1334) |
255 | return -ENOMEM; |
256 | |
257 | platform_set_drvdata(pdev, data: uda1334); |
258 | |
259 | uda1334->mute = devm_gpiod_get(dev: &pdev->dev, con_id: "nxp,mute" , flags: GPIOD_OUT_LOW); |
260 | if (IS_ERR(ptr: uda1334->mute)) { |
261 | ret = PTR_ERR(ptr: uda1334->mute); |
262 | dev_err(&pdev->dev, "Failed to get mute line: %d\n" , ret); |
263 | return ret; |
264 | } |
265 | |
266 | uda1334->deemph = devm_gpiod_get(dev: &pdev->dev, con_id: "nxp,deemph" , flags: GPIOD_OUT_LOW); |
267 | if (IS_ERR(ptr: uda1334->deemph)) { |
268 | ret = PTR_ERR(ptr: uda1334->deemph); |
269 | dev_err(&pdev->dev, "Failed to get deemph line: %d\n" , ret); |
270 | return ret; |
271 | } |
272 | |
273 | ret = devm_snd_soc_register_component(dev: &pdev->dev, |
274 | component_driver: &soc_component_dev_uda1334, |
275 | dai_drv: &uda1334_dai, num_dai: 1); |
276 | if (ret < 0) |
277 | dev_err(&pdev->dev, "Failed to register component: %d\n" , ret); |
278 | |
279 | return ret; |
280 | } |
281 | |
282 | static struct platform_driver uda1334_codec_driver = { |
283 | .probe = uda1334_codec_probe, |
284 | .driver = { |
285 | .name = "uda1334-codec" , |
286 | .of_match_table = uda1334_of_match, |
287 | }, |
288 | }; |
289 | module_platform_driver(uda1334_codec_driver); |
290 | |
291 | MODULE_DESCRIPTION("ASoC UDA1334 driver" ); |
292 | MODULE_AUTHOR("Andra Danciu <andradanciu1997@gmail.com>" ); |
293 | MODULE_ALIAS("platform:uda1334-codec" ); |
294 | MODULE_LICENSE("GPL v2" ); |
295 | |