1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Socionext UniPhier EVEA ADC/DAC codec driver. |
4 | // |
5 | // Copyright (c) 2016-2017 Socionext Inc. |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> |
10 | #include <linux/regmap.h> |
11 | #include <linux/reset.h> |
12 | #include <sound/pcm.h> |
13 | #include <sound/soc.h> |
14 | |
15 | #define DRV_NAME "evea" |
16 | #define EVEA_RATES SNDRV_PCM_RATE_48000 |
17 | #define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE |
18 | |
19 | #define AADCPOW(n) (0x0078 + 0x04 * (n)) |
20 | #define AADCPOW_AADC_POWD BIT(0) |
21 | #define ALINSW1 0x0088 |
22 | #define ALINSW1_SEL1_SHIFT 3 |
23 | #define AHPOUTPOW 0x0098 |
24 | #define AHPOUTPOW_HP_ON BIT(4) |
25 | #define ALINEPOW 0x009c |
26 | #define ALINEPOW_LIN2_POWD BIT(3) |
27 | #define ALINEPOW_LIN1_POWD BIT(4) |
28 | #define ALO1OUTPOW 0x00a8 |
29 | #define ALO1OUTPOW_LO1_ON BIT(4) |
30 | #define ALO2OUTPOW 0x00ac |
31 | #define ALO2OUTPOW_ADAC2_MUTE BIT(0) |
32 | #define ALO2OUTPOW_LO2_ON BIT(4) |
33 | #define AANAPOW 0x00b8 |
34 | #define AANAPOW_A_POWD BIT(4) |
35 | #define ADACSEQ1(n) (0x0144 + 0x40 * (n)) |
36 | #define ADACSEQ1_MMUTE BIT(1) |
37 | #define ADACSEQ2(n) (0x0160 + 0x40 * (n)) |
38 | #define ADACSEQ2_ADACIN_FIX BIT(0) |
39 | #define ADAC1ODC 0x0200 |
40 | #define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1) |
41 | #define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1) |
42 | #define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1) |
43 | #define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7) |
44 | #define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7) |
45 | #define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7) |
46 | |
47 | struct evea_priv { |
48 | struct clk *clk, *clk_exiv; |
49 | struct reset_control *rst, *rst_exiv, *rst_adamv; |
50 | struct regmap *regmap; |
51 | |
52 | int switch_lin; |
53 | int switch_lo; |
54 | int switch_hp; |
55 | }; |
56 | |
57 | static const char * const linsw1_sel1_text[] = { |
58 | "LIN1" , "LIN2" , "LIN3" |
59 | }; |
60 | |
61 | static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum, |
62 | ALINSW1, ALINSW1_SEL1_SHIFT, |
63 | linsw1_sel1_text); |
64 | |
65 | static const struct snd_kcontrol_new linesw1_mux[] = { |
66 | SOC_DAPM_ENUM("Line In 1 Source" , linsw1_sel1_enum), |
67 | }; |
68 | |
69 | static const struct snd_soc_dapm_widget evea_widgets[] = { |
70 | SND_SOC_DAPM_ADC("ADC" , NULL, SND_SOC_NOPM, 0, 0), |
71 | SND_SOC_DAPM_MUX("Line In 1 Mux" , SND_SOC_NOPM, 0, 0, linesw1_mux), |
72 | SND_SOC_DAPM_INPUT("LIN1_LP" ), |
73 | SND_SOC_DAPM_INPUT("LIN1_RP" ), |
74 | SND_SOC_DAPM_INPUT("LIN2_LP" ), |
75 | SND_SOC_DAPM_INPUT("LIN2_RP" ), |
76 | SND_SOC_DAPM_INPUT("LIN3_LP" ), |
77 | SND_SOC_DAPM_INPUT("LIN3_RP" ), |
78 | |
79 | SND_SOC_DAPM_DAC("DAC HP" , NULL, SND_SOC_NOPM, 0, 0), |
80 | SND_SOC_DAPM_DAC("DAC LO1" , NULL, SND_SOC_NOPM, 0, 0), |
81 | SND_SOC_DAPM_DAC("DAC LO2" , NULL, SND_SOC_NOPM, 0, 0), |
82 | SND_SOC_DAPM_OUTPUT("HP1_L" ), |
83 | SND_SOC_DAPM_OUTPUT("HP1_R" ), |
84 | SND_SOC_DAPM_OUTPUT("LO2_L" ), |
85 | SND_SOC_DAPM_OUTPUT("LO2_R" ), |
86 | }; |
87 | |
88 | static const struct snd_soc_dapm_route evea_routes[] = { |
89 | { "Line In 1" , NULL, "ADC" }, |
90 | { "ADC" , NULL, "Line In 1 Mux" }, |
91 | { "Line In 1 Mux" , "LIN1" , "LIN1_LP" }, |
92 | { "Line In 1 Mux" , "LIN1" , "LIN1_RP" }, |
93 | { "Line In 1 Mux" , "LIN2" , "LIN2_LP" }, |
94 | { "Line In 1 Mux" , "LIN2" , "LIN2_RP" }, |
95 | { "Line In 1 Mux" , "LIN3" , "LIN3_LP" }, |
96 | { "Line In 1 Mux" , "LIN3" , "LIN3_RP" }, |
97 | |
98 | { "DAC HP" , NULL, "Headphone 1" }, |
99 | { "DAC LO1" , NULL, "Line Out 1" }, |
100 | { "DAC LO2" , NULL, "Line Out 2" }, |
101 | { "HP1_L" , NULL, "DAC HP" }, |
102 | { "HP1_R" , NULL, "DAC HP" }, |
103 | { "LO2_L" , NULL, "DAC LO2" }, |
104 | { "LO2_R" , NULL, "DAC LO2" }, |
105 | }; |
106 | |
107 | static void evea_set_power_state_on(struct evea_priv *evea) |
108 | { |
109 | struct regmap *map = evea->regmap; |
110 | |
111 | regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD, |
112 | AANAPOW_A_POWD); |
113 | |
114 | regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, |
115 | ADAC1ODC_HP_DIS_RES_ON); |
116 | |
117 | regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK, |
118 | ADAC1ODC_ADAC_RAMPCLT_REDUCE); |
119 | |
120 | regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, val: 0); |
121 | regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, val: 0); |
122 | regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, val: 0); |
123 | } |
124 | |
125 | static void evea_set_power_state_off(struct evea_priv *evea) |
126 | { |
127 | struct regmap *map = evea->regmap; |
128 | |
129 | regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, |
130 | ADAC1ODC_HP_DIS_RES_ON); |
131 | |
132 | regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, |
133 | ADACSEQ1_MMUTE); |
134 | regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, |
135 | ADACSEQ1_MMUTE); |
136 | regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, |
137 | ADACSEQ1_MMUTE); |
138 | |
139 | regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, val: 0); |
140 | regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, val: 0); |
141 | regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, val: 0); |
142 | } |
143 | |
144 | static int evea_update_switch_lin(struct evea_priv *evea) |
145 | { |
146 | struct regmap *map = evea->regmap; |
147 | |
148 | if (evea->switch_lin) { |
149 | regmap_update_bits(map, ALINEPOW, |
150 | ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, |
151 | ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD); |
152 | |
153 | regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, |
154 | AADCPOW_AADC_POWD); |
155 | regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, |
156 | AADCPOW_AADC_POWD); |
157 | } else { |
158 | regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, val: 0); |
159 | regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, val: 0); |
160 | |
161 | regmap_update_bits(map, ALINEPOW, |
162 | ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, val: 0); |
163 | } |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static int evea_update_switch_lo(struct evea_priv *evea) |
169 | { |
170 | struct regmap *map = evea->regmap; |
171 | |
172 | if (evea->switch_lo) { |
173 | regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, val: 0); |
174 | regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, val: 0); |
175 | |
176 | regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, |
177 | ALO1OUTPOW_LO1_ON); |
178 | regmap_update_bits(map, ALO2OUTPOW, |
179 | ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, |
180 | ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON); |
181 | } else { |
182 | regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, |
183 | ADACSEQ1_MMUTE); |
184 | regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, |
185 | ADACSEQ1_MMUTE); |
186 | |
187 | regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, val: 0); |
188 | regmap_update_bits(map, ALO2OUTPOW, |
189 | ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, |
190 | val: 0); |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static int evea_update_switch_hp(struct evea_priv *evea) |
197 | { |
198 | struct regmap *map = evea->regmap; |
199 | |
200 | if (evea->switch_hp) { |
201 | regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, val: 0); |
202 | |
203 | regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, |
204 | AHPOUTPOW_HP_ON); |
205 | |
206 | regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, |
207 | ADAC1ODC_HP_DIS_RES_OFF); |
208 | } else { |
209 | regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, |
210 | ADAC1ODC_HP_DIS_RES_ON); |
211 | |
212 | regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, |
213 | ADACSEQ1_MMUTE); |
214 | |
215 | regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, val: 0); |
216 | } |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static void evea_update_switch_all(struct evea_priv *evea) |
222 | { |
223 | evea_update_switch_lin(evea); |
224 | evea_update_switch_lo(evea); |
225 | evea_update_switch_hp(evea); |
226 | } |
227 | |
228 | static int evea_get_switch_lin(struct snd_kcontrol *kcontrol, |
229 | struct snd_ctl_elem_value *ucontrol) |
230 | { |
231 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
232 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
233 | |
234 | ucontrol->value.integer.value[0] = evea->switch_lin; |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | static int evea_set_switch_lin(struct snd_kcontrol *kcontrol, |
240 | struct snd_ctl_elem_value *ucontrol) |
241 | { |
242 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
243 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
244 | |
245 | if (evea->switch_lin == ucontrol->value.integer.value[0]) |
246 | return 0; |
247 | |
248 | evea->switch_lin = ucontrol->value.integer.value[0]; |
249 | |
250 | return evea_update_switch_lin(evea); |
251 | } |
252 | |
253 | static int evea_get_switch_lo(struct snd_kcontrol *kcontrol, |
254 | struct snd_ctl_elem_value *ucontrol) |
255 | { |
256 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
257 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
258 | |
259 | ucontrol->value.integer.value[0] = evea->switch_lo; |
260 | |
261 | return 0; |
262 | } |
263 | |
264 | static int evea_set_switch_lo(struct snd_kcontrol *kcontrol, |
265 | struct snd_ctl_elem_value *ucontrol) |
266 | { |
267 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
268 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
269 | |
270 | if (evea->switch_lo == ucontrol->value.integer.value[0]) |
271 | return 0; |
272 | |
273 | evea->switch_lo = ucontrol->value.integer.value[0]; |
274 | |
275 | return evea_update_switch_lo(evea); |
276 | } |
277 | |
278 | static int evea_get_switch_hp(struct snd_kcontrol *kcontrol, |
279 | struct snd_ctl_elem_value *ucontrol) |
280 | { |
281 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
282 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
283 | |
284 | ucontrol->value.integer.value[0] = evea->switch_hp; |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | static int evea_set_switch_hp(struct snd_kcontrol *kcontrol, |
290 | struct snd_ctl_elem_value *ucontrol) |
291 | { |
292 | struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
293 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
294 | |
295 | if (evea->switch_hp == ucontrol->value.integer.value[0]) |
296 | return 0; |
297 | |
298 | evea->switch_hp = ucontrol->value.integer.value[0]; |
299 | |
300 | return evea_update_switch_hp(evea); |
301 | } |
302 | |
303 | static const struct snd_kcontrol_new evea_controls[] = { |
304 | SOC_SINGLE_BOOL_EXT("Line Capture Switch" , 0, |
305 | evea_get_switch_lin, evea_set_switch_lin), |
306 | SOC_SINGLE_BOOL_EXT("Line Playback Switch" , 0, |
307 | evea_get_switch_lo, evea_set_switch_lo), |
308 | SOC_SINGLE_BOOL_EXT("Headphone Playback Switch" , 0, |
309 | evea_get_switch_hp, evea_set_switch_hp), |
310 | }; |
311 | |
312 | static int evea_codec_probe(struct snd_soc_component *component) |
313 | { |
314 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
315 | |
316 | evea->switch_lin = 1; |
317 | evea->switch_lo = 1; |
318 | evea->switch_hp = 1; |
319 | |
320 | evea_set_power_state_on(evea); |
321 | evea_update_switch_all(evea); |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static int evea_codec_suspend(struct snd_soc_component *component) |
327 | { |
328 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
329 | |
330 | evea_set_power_state_off(evea); |
331 | |
332 | reset_control_assert(rstc: evea->rst_adamv); |
333 | reset_control_assert(rstc: evea->rst_exiv); |
334 | reset_control_assert(rstc: evea->rst); |
335 | |
336 | clk_disable_unprepare(clk: evea->clk_exiv); |
337 | clk_disable_unprepare(clk: evea->clk); |
338 | |
339 | return 0; |
340 | } |
341 | |
342 | static int evea_codec_resume(struct snd_soc_component *component) |
343 | { |
344 | struct evea_priv *evea = snd_soc_component_get_drvdata(c: component); |
345 | int ret; |
346 | |
347 | ret = clk_prepare_enable(clk: evea->clk); |
348 | if (ret) |
349 | return ret; |
350 | |
351 | ret = clk_prepare_enable(clk: evea->clk_exiv); |
352 | if (ret) |
353 | goto err_out_clock; |
354 | |
355 | ret = reset_control_deassert(rstc: evea->rst); |
356 | if (ret) |
357 | goto err_out_clock_exiv; |
358 | |
359 | ret = reset_control_deassert(rstc: evea->rst_exiv); |
360 | if (ret) |
361 | goto err_out_reset; |
362 | |
363 | ret = reset_control_deassert(rstc: evea->rst_adamv); |
364 | if (ret) |
365 | goto err_out_reset_exiv; |
366 | |
367 | evea_set_power_state_on(evea); |
368 | evea_update_switch_all(evea); |
369 | |
370 | return 0; |
371 | |
372 | err_out_reset_exiv: |
373 | reset_control_assert(rstc: evea->rst_exiv); |
374 | |
375 | err_out_reset: |
376 | reset_control_assert(rstc: evea->rst); |
377 | |
378 | err_out_clock_exiv: |
379 | clk_disable_unprepare(clk: evea->clk_exiv); |
380 | |
381 | err_out_clock: |
382 | clk_disable_unprepare(clk: evea->clk); |
383 | |
384 | return ret; |
385 | } |
386 | |
387 | static struct snd_soc_component_driver soc_codec_evea = { |
388 | .probe = evea_codec_probe, |
389 | .suspend = evea_codec_suspend, |
390 | .resume = evea_codec_resume, |
391 | .dapm_widgets = evea_widgets, |
392 | .num_dapm_widgets = ARRAY_SIZE(evea_widgets), |
393 | .dapm_routes = evea_routes, |
394 | .num_dapm_routes = ARRAY_SIZE(evea_routes), |
395 | .controls = evea_controls, |
396 | .num_controls = ARRAY_SIZE(evea_controls), |
397 | .idle_bias_on = 1, |
398 | .use_pmdown_time = 1, |
399 | .endianness = 1, |
400 | }; |
401 | |
402 | static struct snd_soc_dai_driver soc_dai_evea[] = { |
403 | { |
404 | .name = DRV_NAME "-line1" , |
405 | .playback = { |
406 | .stream_name = "Line Out 1" , |
407 | .formats = EVEA_FORMATS, |
408 | .rates = EVEA_RATES, |
409 | .channels_min = 2, |
410 | .channels_max = 2, |
411 | }, |
412 | .capture = { |
413 | .stream_name = "Line In 1" , |
414 | .formats = EVEA_FORMATS, |
415 | .rates = EVEA_RATES, |
416 | .channels_min = 2, |
417 | .channels_max = 2, |
418 | }, |
419 | }, |
420 | { |
421 | .name = DRV_NAME "-hp1" , |
422 | .playback = { |
423 | .stream_name = "Headphone 1" , |
424 | .formats = EVEA_FORMATS, |
425 | .rates = EVEA_RATES, |
426 | .channels_min = 2, |
427 | .channels_max = 2, |
428 | }, |
429 | }, |
430 | { |
431 | .name = DRV_NAME "-lo2" , |
432 | .playback = { |
433 | .stream_name = "Line Out 2" , |
434 | .formats = EVEA_FORMATS, |
435 | .rates = EVEA_RATES, |
436 | .channels_min = 2, |
437 | .channels_max = 2, |
438 | }, |
439 | }, |
440 | }; |
441 | |
442 | static const struct regmap_config evea_regmap_config = { |
443 | .reg_bits = 32, |
444 | .reg_stride = 4, |
445 | .val_bits = 32, |
446 | .max_register = 0xffc, |
447 | .cache_type = REGCACHE_NONE, |
448 | }; |
449 | |
450 | static int evea_probe(struct platform_device *pdev) |
451 | { |
452 | struct evea_priv *evea; |
453 | void __iomem *preg; |
454 | int ret; |
455 | |
456 | evea = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct evea_priv), GFP_KERNEL); |
457 | if (!evea) |
458 | return -ENOMEM; |
459 | |
460 | evea->clk = devm_clk_get(dev: &pdev->dev, id: "evea" ); |
461 | if (IS_ERR(ptr: evea->clk)) |
462 | return PTR_ERR(ptr: evea->clk); |
463 | |
464 | evea->clk_exiv = devm_clk_get(dev: &pdev->dev, id: "exiv" ); |
465 | if (IS_ERR(ptr: evea->clk_exiv)) |
466 | return PTR_ERR(ptr: evea->clk_exiv); |
467 | |
468 | evea->rst = devm_reset_control_get_shared(dev: &pdev->dev, id: "evea" ); |
469 | if (IS_ERR(ptr: evea->rst)) |
470 | return PTR_ERR(ptr: evea->rst); |
471 | |
472 | evea->rst_exiv = devm_reset_control_get_shared(dev: &pdev->dev, id: "exiv" ); |
473 | if (IS_ERR(ptr: evea->rst_exiv)) |
474 | return PTR_ERR(ptr: evea->rst_exiv); |
475 | |
476 | preg = devm_platform_ioremap_resource(pdev, index: 0); |
477 | if (IS_ERR(ptr: preg)) |
478 | return PTR_ERR(ptr: preg); |
479 | |
480 | evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg, |
481 | &evea_regmap_config); |
482 | if (IS_ERR(ptr: evea->regmap)) |
483 | return PTR_ERR(ptr: evea->regmap); |
484 | |
485 | ret = clk_prepare_enable(clk: evea->clk); |
486 | if (ret) |
487 | return ret; |
488 | |
489 | ret = clk_prepare_enable(clk: evea->clk_exiv); |
490 | if (ret) |
491 | goto err_out_clock; |
492 | |
493 | ret = reset_control_deassert(rstc: evea->rst); |
494 | if (ret) |
495 | goto err_out_clock_exiv; |
496 | |
497 | ret = reset_control_deassert(rstc: evea->rst_exiv); |
498 | if (ret) |
499 | goto err_out_reset; |
500 | |
501 | /* ADAMV will hangup if EXIV reset is asserted */ |
502 | evea->rst_adamv = devm_reset_control_get_shared(dev: &pdev->dev, id: "adamv" ); |
503 | if (IS_ERR(ptr: evea->rst_adamv)) { |
504 | ret = PTR_ERR(ptr: evea->rst_adamv); |
505 | goto err_out_reset_exiv; |
506 | } |
507 | |
508 | ret = reset_control_deassert(rstc: evea->rst_adamv); |
509 | if (ret) |
510 | goto err_out_reset_exiv; |
511 | |
512 | platform_set_drvdata(pdev, data: evea); |
513 | |
514 | ret = devm_snd_soc_register_component(dev: &pdev->dev, component_driver: &soc_codec_evea, |
515 | dai_drv: soc_dai_evea, ARRAY_SIZE(soc_dai_evea)); |
516 | if (ret) |
517 | goto err_out_reset_adamv; |
518 | |
519 | return 0; |
520 | |
521 | err_out_reset_adamv: |
522 | reset_control_assert(rstc: evea->rst_adamv); |
523 | |
524 | err_out_reset_exiv: |
525 | reset_control_assert(rstc: evea->rst_exiv); |
526 | |
527 | err_out_reset: |
528 | reset_control_assert(rstc: evea->rst); |
529 | |
530 | err_out_clock_exiv: |
531 | clk_disable_unprepare(clk: evea->clk_exiv); |
532 | |
533 | err_out_clock: |
534 | clk_disable_unprepare(clk: evea->clk); |
535 | |
536 | return ret; |
537 | } |
538 | |
539 | static void evea_remove(struct platform_device *pdev) |
540 | { |
541 | struct evea_priv *evea = platform_get_drvdata(pdev); |
542 | |
543 | reset_control_assert(rstc: evea->rst_adamv); |
544 | reset_control_assert(rstc: evea->rst_exiv); |
545 | reset_control_assert(rstc: evea->rst); |
546 | |
547 | clk_disable_unprepare(clk: evea->clk_exiv); |
548 | clk_disable_unprepare(clk: evea->clk); |
549 | } |
550 | |
551 | static const struct of_device_id evea_of_match[] __maybe_unused = { |
552 | { .compatible = "socionext,uniphier-evea" , }, |
553 | {} |
554 | }; |
555 | MODULE_DEVICE_TABLE(of, evea_of_match); |
556 | |
557 | static struct platform_driver evea_codec_driver = { |
558 | .driver = { |
559 | .name = DRV_NAME, |
560 | .of_match_table = of_match_ptr(evea_of_match), |
561 | }, |
562 | .probe = evea_probe, |
563 | .remove_new = evea_remove, |
564 | }; |
565 | module_platform_driver(evea_codec_driver); |
566 | |
567 | MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>" ); |
568 | MODULE_DESCRIPTION("UniPhier EVEA codec driver" ); |
569 | MODULE_LICENSE("GPL v2" ); |
570 | |