diff options
author | Andra Danciu <andradanciu1997@gmail.com> | 2019-07-31 07:19:30 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2019-07-31 07:26:47 -0400 |
commit | caa918ef14065b812737f3ee4ac349dcfff196e6 (patch) | |
tree | 368af04566fa00497db0953e17173c55cc44d5e5 | |
parent | d6de65fde51644f6ed6b1c0c05fef6a2da5ff768 (diff) |
ASoC: codecs: Add uda1334 codec driver
The UDA1334BTS supports the I2S-bus data format with word lengths of up
to 24 bits serial data format and has basic features such as de-emphasis
(at 44.1 kHz sampling rate) and mute.
Datasheet can be found at:
https://www.nxp.com/docs/en/data-sheet/UDA1334BTS.pdf
Cc: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Andra Danciu <andradanciu1997@gmail.com>
Link: https://lore.kernel.org/r/20190731111930.20230-3-andradanciu1997@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/codecs/Kconfig | 9 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/uda1334.c | 295 |
3 files changed, 306 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index da4c1ae89742..89238343e34d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -199,6 +199,7 @@ config SND_SOC_ALL_CODECS | |||
199 | select SND_SOC_TS3A227E if I2C | 199 | select SND_SOC_TS3A227E if I2C |
200 | select SND_SOC_TWL4030 if TWL4030_CORE | 200 | select SND_SOC_TWL4030 if TWL4030_CORE |
201 | select SND_SOC_TWL6040 if TWL6040_CORE | 201 | select SND_SOC_TWL6040 if TWL6040_CORE |
202 | select SND_SOC_UDA1334 if GPIOLIB | ||
202 | select SND_SOC_UDA134X | 203 | select SND_SOC_UDA134X |
203 | select SND_SOC_UDA1380 if I2C | 204 | select SND_SOC_UDA1380 if I2C |
204 | select SND_SOC_WCD9335 if SLIMBUS | 205 | select SND_SOC_WCD9335 if SLIMBUS |
@@ -1207,6 +1208,14 @@ config SND_SOC_TWL4030 | |||
1207 | config SND_SOC_TWL6040 | 1208 | config SND_SOC_TWL6040 |
1208 | tristate | 1209 | tristate |
1209 | 1210 | ||
1211 | config SND_SOC_UDA1334 | ||
1212 | tristate "NXP UDA1334 DAC" | ||
1213 | depends on GPIOLIB | ||
1214 | help | ||
1215 | The UDA1334 is an NXP audio codec, supports the I2S-bus data format | ||
1216 | and has basic features such as de-emphasis (at 44.1 kHz sampling | ||
1217 | rate) and mute. | ||
1218 | |||
1210 | config SND_SOC_UDA134X | 1219 | config SND_SOC_UDA134X |
1211 | tristate | 1220 | tristate |
1212 | 1221 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9230016b0f9f..c498373dcc5f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -212,6 +212,7 @@ snd-soc-tscs454-objs := tscs454.o | |||
212 | snd-soc-ts3a227e-objs := ts3a227e.o | 212 | snd-soc-ts3a227e-objs := ts3a227e.o |
213 | snd-soc-twl4030-objs := twl4030.o | 213 | snd-soc-twl4030-objs := twl4030.o |
214 | snd-soc-twl6040-objs := twl6040.o | 214 | snd-soc-twl6040-objs := twl6040.o |
215 | snd-soc-uda1334-objs := uda1334.o | ||
215 | snd-soc-uda134x-objs := uda134x.o | 216 | snd-soc-uda134x-objs := uda134x.o |
216 | snd-soc-uda1380-objs := uda1380.o | 217 | snd-soc-uda1380-objs := uda1380.o |
217 | snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o | 218 | snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o |
@@ -494,6 +495,7 @@ obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o | |||
494 | obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o | 495 | obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o |
495 | obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o | 496 | obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o |
496 | obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o | 497 | obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o |
498 | obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o | ||
497 | obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o | 499 | obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o |
498 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o | 500 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o |
499 | obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o | 501 | obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o |
diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c new file mode 100644 index 000000000000..21ab8c5487ba --- /dev/null +++ b/sound/soc/codecs/uda1334.c | |||
@@ -0,0 +1,295 @@ | |||
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/module.h> | ||
8 | #include <linux/moduleparam.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/gpio/consumer.h> | ||
13 | #include <linux/of_device.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(component); | ||
47 | int deemph = ucontrol->value.integer.value[0]; | ||
48 | |||
49 | if (deemph > 1) | ||
50 | return -EINVAL; | ||
51 | |||
52 | gpiod_set_value_cansleep(uda1334->deemph, 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(component); | ||
62 | int ret; | ||
63 | |||
64 | ret = gpiod_get_value_cansleep(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(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(substream->runtime, 0, | ||
107 | SNDRV_PCM_HW_PARAM_RATE, | ||
108 | &uda1334->rate_constraint); | ||
109 | |||
110 | gpiod_set_value_cansleep(uda1334->mute, 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(component); | ||
120 | |||
121 | gpiod_set_value_cansleep(uda1334->mute, 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(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_MASTER_MASK); | ||
173 | |||
174 | if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
175 | SND_SOC_DAIFMT_CBS_CFS)) { | ||
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(dai->component); | ||
186 | |||
187 | if (uda1334->mute) | ||
188 | gpiod_set_value_cansleep(uda1334->mute, 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(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 | .non_legacy_dai_naming = 1, | ||
240 | }; | ||
241 | |||
242 | static const struct of_device_id uda1334_of_match[] = { | ||
243 | { .compatible = "nxp,uda1334" }, | ||
244 | { /* sentinel*/ } | ||
245 | }; | ||
246 | MODULE_DEVICE_TABLE(of, uda1334_of_match); | ||
247 | |||
248 | static int uda1334_codec_probe(struct platform_device *pdev) | ||
249 | { | ||
250 | struct uda1334_priv *uda1334; | ||
251 | int ret; | ||
252 | |||
253 | uda1334 = devm_kzalloc(&pdev->dev, sizeof(struct uda1334_priv), | ||
254 | GFP_KERNEL); | ||
255 | if (!uda1334) | ||
256 | return -ENOMEM; | ||
257 | |||
258 | platform_set_drvdata(pdev, uda1334); | ||
259 | |||
260 | uda1334->mute = devm_gpiod_get(&pdev->dev, "nxp,mute", GPIOD_OUT_LOW); | ||
261 | if (IS_ERR(uda1334->mute)) { | ||
262 | ret = PTR_ERR(uda1334->mute); | ||
263 | dev_err(&pdev->dev, "Failed to get mute line: %d\n", ret); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | uda1334->deemph = devm_gpiod_get(&pdev->dev, "nxp,deemph", GPIOD_OUT_LOW); | ||
268 | if (IS_ERR(uda1334->deemph)) { | ||
269 | ret = PTR_ERR(uda1334->deemph); | ||
270 | dev_err(&pdev->dev, "Failed to get deemph line: %d\n", ret); | ||
271 | return ret; | ||
272 | } | ||
273 | |||
274 | ret = devm_snd_soc_register_component(&pdev->dev, | ||
275 | &soc_component_dev_uda1334, | ||
276 | &uda1334_dai, 1); | ||
277 | if (ret < 0) | ||
278 | dev_err(&pdev->dev, "Failed to register component: %d\n", ret); | ||
279 | |||
280 | return ret; | ||
281 | } | ||
282 | |||
283 | static struct platform_driver uda1334_codec_driver = { | ||
284 | .probe = uda1334_codec_probe, | ||
285 | .driver = { | ||
286 | .name = "uda1334-codec", | ||
287 | .of_match_table = uda1334_of_match, | ||
288 | }, | ||
289 | }; | ||
290 | module_platform_driver(uda1334_codec_driver); | ||
291 | |||
292 | MODULE_DESCRIPTION("ASoC UDA1334 driver"); | ||
293 | MODULE_AUTHOR("Andra Danciu <andradanciu1997@gmail.com>"); | ||
294 | MODULE_ALIAS("platform:uda1334-codec"); | ||
295 | MODULE_LICENSE("GPL v2"); | ||