1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // TI SRC4xxx Audio Codec driver |
4 | // |
5 | // Copyright 2021-2022 Deqx Pty Ltd |
6 | // Author: Matt Flax <flatmax@flatmax.com> |
7 | |
8 | #include <linux/module.h> |
9 | |
10 | #include <sound/soc.h> |
11 | #include <sound/tlv.h> |
12 | |
13 | #include "src4xxx.h" |
14 | |
15 | struct src4xxx { |
16 | struct regmap *regmap; |
17 | bool master[2]; |
18 | int mclk_hz; |
19 | struct device *dev; |
20 | }; |
21 | |
22 | enum {SRC4XXX_PORTA, SRC4XXX_PORTB}; |
23 | |
24 | /* SRC attenuation */ |
25 | static const DECLARE_TLV_DB_SCALE(src_tlv, -12750, 50, 0); |
26 | |
27 | static const struct snd_kcontrol_new src4xxx_controls[] = { |
28 | SOC_DOUBLE_R_TLV("SRC Volume" , |
29 | SRC4XXX_SCR_CTL_30, SRC4XXX_SCR_CTL_31, 0, 255, 1, src_tlv), |
30 | }; |
31 | |
32 | /* I2S port control */ |
33 | static const char * const port_out_src_text[] = { |
34 | "loopback" , "other_port" , "DIR" , "SRC" |
35 | }; |
36 | static SOC_ENUM_SINGLE_DECL(porta_out_src_enum, SRC4XXX_PORTA_CTL_03, 4, |
37 | port_out_src_text); |
38 | static SOC_ENUM_SINGLE_DECL(portb_out_src_enum, SRC4XXX_PORTB_CTL_05, 4, |
39 | port_out_src_text); |
40 | static const struct snd_kcontrol_new porta_out_control = |
41 | SOC_DAPM_ENUM("Port A source select" , porta_out_src_enum); |
42 | static const struct snd_kcontrol_new portb_out_control = |
43 | SOC_DAPM_ENUM("Port B source select" , portb_out_src_enum); |
44 | |
45 | /* Digital audio transmitter control */ |
46 | static const char * const dit_mux_text[] = {"Port A" , "Port B" , "DIR" , "SRC" }; |
47 | static SOC_ENUM_SINGLE_DECL(dit_mux_enum, SRC4XXX_TX_CTL_07, 3, dit_mux_text); |
48 | static const struct snd_kcontrol_new dit_mux_control = |
49 | SOC_DAPM_ENUM("DIT source" , dit_mux_enum); |
50 | |
51 | /* SRC control */ |
52 | static const char * const src_in_text[] = {"Port A" , "Port B" , "DIR" }; |
53 | static SOC_ENUM_SINGLE_DECL(src_in_enum, SRC4XXX_SCR_CTL_2D, 0, src_in_text); |
54 | static const struct snd_kcontrol_new src_in_control = |
55 | SOC_DAPM_ENUM("SRC source select" , src_in_enum); |
56 | |
57 | /* DIR control */ |
58 | static const char * const dir_in_text[] = {"Ch 1" , "Ch 2" , "Ch 3" , "Ch 4" }; |
59 | static SOC_ENUM_SINGLE_DECL(dir_in_enum, SRC4XXX_RCV_CTL_0D, 0, dir_in_text); |
60 | static const struct snd_kcontrol_new dir_in_control = |
61 | SOC_DAPM_ENUM("Digital Input" , dir_in_enum); |
62 | |
63 | static const struct snd_soc_dapm_widget src4xxx_dapm_widgets[] = { |
64 | SND_SOC_DAPM_INPUT("loopback_A" ), |
65 | SND_SOC_DAPM_INPUT("other_port_A" ), |
66 | SND_SOC_DAPM_INPUT("DIR_A" ), |
67 | SND_SOC_DAPM_INPUT("SRC_A" ), |
68 | SND_SOC_DAPM_MUX("Port A source" , |
69 | SND_SOC_NOPM, 0, 0, &porta_out_control), |
70 | |
71 | SND_SOC_DAPM_INPUT("loopback_B" ), |
72 | SND_SOC_DAPM_INPUT("other_port_B" ), |
73 | SND_SOC_DAPM_INPUT("DIR_B" ), |
74 | SND_SOC_DAPM_INPUT("SRC_B" ), |
75 | SND_SOC_DAPM_MUX("Port B source" , |
76 | SND_SOC_NOPM, 0, 0, &portb_out_control), |
77 | |
78 | SND_SOC_DAPM_INPUT("Port_A" ), |
79 | SND_SOC_DAPM_INPUT("Port_B" ), |
80 | SND_SOC_DAPM_INPUT("DIR_" ), |
81 | |
82 | /* Digital audio receivers and transmitters */ |
83 | SND_SOC_DAPM_OUTPUT("DIR_OUT" ), |
84 | SND_SOC_DAPM_OUTPUT("SRC_OUT" ), |
85 | SND_SOC_DAPM_MUX("DIT Out Src" , SRC4XXX_PWR_RST_01, |
86 | SRC4XXX_ENABLE_DIT_SHIFT, 1, &dit_mux_control), |
87 | |
88 | /* Audio Interface */ |
89 | SND_SOC_DAPM_AIF_IN("AIF_A_RX" , "Playback A" , 0, |
90 | SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1), |
91 | SND_SOC_DAPM_AIF_OUT("AIF_A_TX" , "Capture A" , 0, |
92 | SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1), |
93 | SND_SOC_DAPM_AIF_IN("AIF_B_RX" , "Playback B" , 0, |
94 | SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1), |
95 | SND_SOC_DAPM_AIF_OUT("AIF_B_TX" , "Capture B" , 0, |
96 | SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1), |
97 | |
98 | SND_SOC_DAPM_MUX("SRC source" , SND_SOC_NOPM, 0, 0, &src_in_control), |
99 | |
100 | SND_SOC_DAPM_INPUT("MCLK" ), |
101 | SND_SOC_DAPM_INPUT("RXMCLKI" ), |
102 | SND_SOC_DAPM_INPUT("RXMCLKO" ), |
103 | |
104 | SND_SOC_DAPM_INPUT("RX1" ), |
105 | SND_SOC_DAPM_INPUT("RX2" ), |
106 | SND_SOC_DAPM_INPUT("RX3" ), |
107 | SND_SOC_DAPM_INPUT("RX4" ), |
108 | SND_SOC_DAPM_MUX("Digital Input" , SRC4XXX_PWR_RST_01, |
109 | SRC4XXX_ENABLE_DIR_SHIFT, 1, &dir_in_control), |
110 | }; |
111 | |
112 | static const struct snd_soc_dapm_route src4xxx_audio_routes[] = { |
113 | /* I2S Input to Output Routing */ |
114 | {"Port A source" , "loopback" , "loopback_A" }, |
115 | {"Port A source" , "other_port" , "other_port_A" }, |
116 | {"Port A source" , "DIR" , "DIR_A" }, |
117 | {"Port A source" , "SRC" , "SRC_A" }, |
118 | {"Port B source" , "loopback" , "loopback_B" }, |
119 | {"Port B source" , "other_port" , "other_port_B" }, |
120 | {"Port B source" , "DIR" , "DIR_B" }, |
121 | {"Port B source" , "SRC" , "SRC_B" }, |
122 | /* DIT muxing */ |
123 | {"DIT Out Src" , "Port A" , "Capture A" }, |
124 | {"DIT Out Src" , "Port B" , "Capture B" }, |
125 | {"DIT Out Src" , "DIR" , "DIR_OUT" }, |
126 | {"DIT Out Src" , "SRC" , "SRC_OUT" }, |
127 | |
128 | /* SRC input selection */ |
129 | {"SRC source" , "Port A" , "Port_A" }, |
130 | {"SRC source" , "Port B" , "Port_B" }, |
131 | {"SRC source" , "DIR" , "DIR_" }, |
132 | /* SRC mclk selection */ |
133 | {"SRC mclk source" , "Master (MCLK)" , "MCLK" }, |
134 | {"SRC mclk source" , "Master (RXCLKI)" , "RXMCLKI" }, |
135 | {"SRC mclk source" , "Recovered receiver clk" , "RXMCLKO" }, |
136 | /* DIR input selection */ |
137 | {"Digital Input" , "Ch 1" , "RX1" }, |
138 | {"Digital Input" , "Ch 2" , "RX2" }, |
139 | {"Digital Input" , "Ch 3" , "RX3" }, |
140 | {"Digital Input" , "Ch 4" , "RX4" }, |
141 | }; |
142 | |
143 | |
144 | static const struct snd_soc_component_driver src4xxx_driver = { |
145 | .controls = src4xxx_controls, |
146 | .num_controls = ARRAY_SIZE(src4xxx_controls), |
147 | |
148 | .dapm_widgets = src4xxx_dapm_widgets, |
149 | .num_dapm_widgets = ARRAY_SIZE(src4xxx_dapm_widgets), |
150 | .dapm_routes = src4xxx_audio_routes, |
151 | .num_dapm_routes = ARRAY_SIZE(src4xxx_audio_routes), |
152 | }; |
153 | |
154 | static int src4xxx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
155 | { |
156 | struct snd_soc_component *component = dai->component; |
157 | struct src4xxx *src4xxx = snd_soc_component_get_drvdata(c: component); |
158 | unsigned int ctrl; |
159 | |
160 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
161 | case SND_SOC_DAIFMT_CBM_CFM: |
162 | ctrl = SRC4XXX_BUS_MASTER; |
163 | src4xxx->master[dai->id] = true; |
164 | break; |
165 | case SND_SOC_DAIFMT_CBS_CFS: |
166 | ctrl = 0; |
167 | src4xxx->master[dai->id] = false; |
168 | break; |
169 | default: |
170 | return -EINVAL; |
171 | break; |
172 | } |
173 | |
174 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
175 | case SND_SOC_DAIFMT_I2S: |
176 | ctrl |= SRC4XXX_BUS_I2S; |
177 | break; |
178 | case SND_SOC_DAIFMT_LEFT_J: |
179 | ctrl |= SRC4XXX_BUS_LEFT_J; |
180 | break; |
181 | case SND_SOC_DAIFMT_RIGHT_J: |
182 | ctrl |= SRC4XXX_BUS_RIGHT_J_24; |
183 | break; |
184 | default: |
185 | return -EINVAL; |
186 | break; |
187 | } |
188 | |
189 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
190 | case SND_SOC_DAIFMT_NB_NF: |
191 | break; |
192 | default: |
193 | return -EINVAL; |
194 | break; |
195 | } |
196 | |
197 | regmap_update_bits(map: src4xxx->regmap, SRC4XXX_BUS_FMT(dai->id), |
198 | SRC4XXX_BUS_FMT_MS_MASK, val: ctrl); |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | static int src4xxx_set_mclk_hz(struct snd_soc_dai *codec_dai, |
204 | int clk_id, unsigned int freq, int dir) |
205 | { |
206 | struct snd_soc_component *component = codec_dai->component; |
207 | struct src4xxx *src4xxx = snd_soc_component_get_drvdata(c: component); |
208 | |
209 | dev_info(component->dev, "changing mclk rate from %d to %d Hz\n" , |
210 | src4xxx->mclk_hz, freq); |
211 | src4xxx->mclk_hz = freq; |
212 | |
213 | return 0; |
214 | } |
215 | |
216 | static int src4xxx_hw_params(struct snd_pcm_substream *substream, |
217 | struct snd_pcm_hw_params *params, |
218 | struct snd_soc_dai *dai) |
219 | { |
220 | struct snd_soc_component *component = dai->component; |
221 | struct src4xxx *src4xxx = snd_soc_component_get_drvdata(c: component); |
222 | unsigned int mclk_div; |
223 | int val, pj, jd, d; |
224 | int reg; |
225 | int ret; |
226 | |
227 | switch (dai->id) { |
228 | case SRC4XXX_PORTB: |
229 | reg = SRC4XXX_PORTB_CTL_06; |
230 | break; |
231 | default: |
232 | reg = SRC4XXX_PORTA_CTL_04; |
233 | break; |
234 | } |
235 | |
236 | if (src4xxx->master[dai->id]) { |
237 | mclk_div = src4xxx->mclk_hz/params_rate(p: params); |
238 | if (src4xxx->mclk_hz != mclk_div*params_rate(p: params)) { |
239 | dev_err(component->dev, |
240 | "mclk %d / rate %d has a remainder.\n" , |
241 | src4xxx->mclk_hz, params_rate(params)); |
242 | return -EINVAL; |
243 | } |
244 | |
245 | val = ((int)mclk_div - 128) / 128; |
246 | if ((val < 0) | (val > 3)) { |
247 | dev_err(component->dev, |
248 | "div register setting %d is out of range\n" , |
249 | val); |
250 | dev_err(component->dev, |
251 | "unsupported sample rate %d Hz for the master clock of %d Hz\n" , |
252 | params_rate(params), src4xxx->mclk_hz); |
253 | return -EINVAL; |
254 | } |
255 | |
256 | /* set the TX DIV */ |
257 | ret = regmap_update_bits(map: src4xxx->regmap, |
258 | SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK, |
259 | val: val<<SRC4XXX_TX_MCLK_DIV_SHIFT); |
260 | if (ret) { |
261 | dev_err(component->dev, |
262 | "Couldn't set the TX's div register to %d << %d = 0x%x\n" , |
263 | val, SRC4XXX_TX_MCLK_DIV_SHIFT, |
264 | val<<SRC4XXX_TX_MCLK_DIV_SHIFT); |
265 | return ret; |
266 | } |
267 | |
268 | /* set the PLL for the digital receiver */ |
269 | switch (src4xxx->mclk_hz) { |
270 | case 24576000: |
271 | pj = 0x22; |
272 | jd = 0x00; |
273 | d = 0x00; |
274 | break; |
275 | case 22579200: |
276 | pj = 0x22; |
277 | jd = 0x1b; |
278 | d = 0xa3; |
279 | break; |
280 | default: |
281 | /* don't error out here, |
282 | * other parts of the chip are still functional |
283 | * Dummy initialize variables to avoid |
284 | * -Wsometimes-uninitialized from clang. |
285 | */ |
286 | dev_info(component->dev, |
287 | "Couldn't set the RCV PLL as this master clock rate is unknown. Chosen regmap values may not match real world values.\n" ); |
288 | pj = 0x0; |
289 | jd = 0xff; |
290 | d = 0xff; |
291 | break; |
292 | } |
293 | ret = regmap_write(map: src4xxx->regmap, SRC4XXX_RCV_PLL_0F, val: pj); |
294 | if (ret < 0) |
295 | dev_err(component->dev, |
296 | "Failed to update PLL register 0x%x\n" , |
297 | SRC4XXX_RCV_PLL_0F); |
298 | ret = regmap_write(map: src4xxx->regmap, SRC4XXX_RCV_PLL_10, val: jd); |
299 | if (ret < 0) |
300 | dev_err(component->dev, |
301 | "Failed to update PLL register 0x%x\n" , |
302 | SRC4XXX_RCV_PLL_10); |
303 | ret = regmap_write(map: src4xxx->regmap, SRC4XXX_RCV_PLL_11, val: d); |
304 | if (ret < 0) |
305 | dev_err(component->dev, |
306 | "Failed to update PLL register 0x%x\n" , |
307 | SRC4XXX_RCV_PLL_11); |
308 | |
309 | ret = regmap_update_bits(map: src4xxx->regmap, |
310 | SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK, |
311 | val: val<<SRC4XXX_TX_MCLK_DIV_SHIFT); |
312 | if (ret < 0) { |
313 | dev_err(component->dev, |
314 | "Couldn't set the TX's div register to %d << %d = 0x%x\n" , |
315 | val, SRC4XXX_TX_MCLK_DIV_SHIFT, |
316 | val<<SRC4XXX_TX_MCLK_DIV_SHIFT); |
317 | return ret; |
318 | } |
319 | |
320 | return regmap_update_bits(map: src4xxx->regmap, reg, |
321 | SRC4XXX_MCLK_DIV_MASK, val); |
322 | } else { |
323 | dev_info(dai->dev, "not setting up MCLK as not master\n" ); |
324 | } |
325 | |
326 | return 0; |
327 | }; |
328 | |
329 | static const struct snd_soc_dai_ops src4xxx_dai_ops = { |
330 | .hw_params = src4xxx_hw_params, |
331 | .set_sysclk = src4xxx_set_mclk_hz, |
332 | .set_fmt = src4xxx_set_dai_fmt, |
333 | }; |
334 | |
335 | #define SRC4XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) |
336 | #define SRC4XXX_RATES (SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000|\ |
337 | SNDRV_PCM_RATE_88200|\ |
338 | SNDRV_PCM_RATE_96000|\ |
339 | SNDRV_PCM_RATE_176400|\ |
340 | SNDRV_PCM_RATE_192000) |
341 | |
342 | static struct snd_soc_dai_driver src4xxx_dai_driver[] = { |
343 | { |
344 | .id = SRC4XXX_PORTA, |
345 | .name = "src4xxx-portA" , |
346 | .playback = { |
347 | .stream_name = "Playback A" , |
348 | .channels_min = 2, |
349 | .channels_max = 2, |
350 | .rates = SRC4XXX_RATES, |
351 | .formats = SRC4XXX_FORMATS, |
352 | }, |
353 | .capture = { |
354 | .stream_name = "Capture A" , |
355 | .channels_min = 2, |
356 | .channels_max = 2, |
357 | .rates = SRC4XXX_RATES, |
358 | .formats = SRC4XXX_FORMATS, |
359 | }, |
360 | .ops = &src4xxx_dai_ops, |
361 | }, |
362 | { |
363 | .id = SRC4XXX_PORTB, |
364 | .name = "src4xxx-portB" , |
365 | .playback = { |
366 | .stream_name = "Playback B" , |
367 | .channels_min = 2, |
368 | .channels_max = 2, |
369 | .rates = SRC4XXX_RATES, |
370 | .formats = SRC4XXX_FORMATS, |
371 | }, |
372 | .capture = { |
373 | .stream_name = "Capture B" , |
374 | .channels_min = 2, |
375 | .channels_max = 2, |
376 | .rates = SRC4XXX_RATES, |
377 | .formats = SRC4XXX_FORMATS, |
378 | }, |
379 | .ops = &src4xxx_dai_ops, |
380 | }, |
381 | }; |
382 | |
383 | static const struct reg_default src4xxx_reg_defaults[] = { |
384 | { SRC4XXX_PWR_RST_01, 0x00 }, /* all powered down intially */ |
385 | { SRC4XXX_PORTA_CTL_03, 0x00 }, |
386 | { SRC4XXX_PORTA_CTL_04, 0x00 }, |
387 | { SRC4XXX_PORTB_CTL_05, 0x00 }, |
388 | { SRC4XXX_PORTB_CTL_06, 0x00 }, |
389 | { SRC4XXX_TX_CTL_07, 0x00 }, |
390 | { SRC4XXX_TX_CTL_08, 0x00 }, |
391 | { SRC4XXX_TX_CTL_09, 0x00 }, |
392 | { SRC4XXX_SRC_DIT_IRQ_MSK_0B, 0x00 }, |
393 | { SRC4XXX_SRC_DIT_IRQ_MODE_0C, 0x00 }, |
394 | { SRC4XXX_RCV_CTL_0D, 0x00 }, |
395 | { SRC4XXX_RCV_CTL_0E, 0x00 }, |
396 | { SRC4XXX_RCV_PLL_0F, 0x00 }, /* not spec. in the datasheet */ |
397 | { SRC4XXX_RCV_PLL_10, 0xff }, /* not spec. in the datasheet */ |
398 | { SRC4XXX_RCV_PLL_11, 0xff }, /* not spec. in the datasheet */ |
399 | { SRC4XXX_RVC_IRQ_MSK_16, 0x00 }, |
400 | { SRC4XXX_RVC_IRQ_MSK_17, 0x00 }, |
401 | { SRC4XXX_RVC_IRQ_MODE_18, 0x00 }, |
402 | { SRC4XXX_RVC_IRQ_MODE_19, 0x00 }, |
403 | { SRC4XXX_RVC_IRQ_MODE_1A, 0x00 }, |
404 | { SRC4XXX_GPIO_1_1B, 0x00 }, |
405 | { SRC4XXX_GPIO_2_1C, 0x00 }, |
406 | { SRC4XXX_GPIO_3_1D, 0x00 }, |
407 | { SRC4XXX_GPIO_4_1E, 0x00 }, |
408 | { SRC4XXX_SCR_CTL_2D, 0x00 }, |
409 | { SRC4XXX_SCR_CTL_2E, 0x00 }, |
410 | { SRC4XXX_SCR_CTL_2F, 0x00 }, |
411 | { SRC4XXX_SCR_CTL_30, 0x00 }, |
412 | { SRC4XXX_SCR_CTL_31, 0x00 }, |
413 | }; |
414 | |
415 | int src4xxx_probe(struct device *dev, struct regmap *regmap, |
416 | void (*switch_mode)(struct device *dev)) |
417 | { |
418 | struct src4xxx *src4xxx; |
419 | int ret; |
420 | |
421 | if (IS_ERR(ptr: regmap)) |
422 | return PTR_ERR(ptr: regmap); |
423 | |
424 | src4xxx = devm_kzalloc(dev, size: sizeof(*src4xxx), GFP_KERNEL); |
425 | if (!src4xxx) |
426 | return -ENOMEM; |
427 | |
428 | src4xxx->regmap = regmap; |
429 | src4xxx->dev = dev; |
430 | src4xxx->mclk_hz = 0; /* mclk has not been configured yet */ |
431 | dev_set_drvdata(dev, data: src4xxx); |
432 | |
433 | ret = regmap_write(map: regmap, SRC4XXX_PWR_RST_01, SRC4XXX_RESET); |
434 | if (ret < 0) |
435 | dev_err(dev, "Failed to issue reset: %d\n" , ret); |
436 | usleep_range(min: 1, max: 500); /* sleep for more then 500 ns */ |
437 | ret = regmap_write(map: regmap, SRC4XXX_PWR_RST_01, SRC4XXX_POWER_DOWN); |
438 | if (ret < 0) |
439 | dev_err(dev, "Failed to decommission reset: %d\n" , ret); |
440 | usleep_range(min: 500, max: 1000); /* sleep for 500 us or more */ |
441 | |
442 | ret = regmap_update_bits(map: src4xxx->regmap, SRC4XXX_PWR_RST_01, |
443 | SRC4XXX_POWER_ENABLE, SRC4XXX_POWER_ENABLE); |
444 | if (ret < 0) |
445 | dev_err(dev, "Failed to port A and B : %d\n" , ret); |
446 | |
447 | /* set receiver to use master clock (rcv mclk is most likely jittery) */ |
448 | ret = regmap_update_bits(map: src4xxx->regmap, SRC4XXX_RCV_CTL_0D, |
449 | SRC4XXX_RXCLK_MCLK, SRC4XXX_RXCLK_MCLK); |
450 | if (ret < 0) |
451 | dev_err(dev, |
452 | "Failed to enable mclk as the PLL1 DIR reference : %d\n" , ret); |
453 | |
454 | /* default to leaving the PLL2 running on loss of lock, divide by 8 */ |
455 | ret = regmap_update_bits(map: src4xxx->regmap, SRC4XXX_RCV_CTL_0E, |
456 | SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL, |
457 | SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL); |
458 | if (ret < 0) |
459 | dev_err(dev, "Failed to enable mclk rec and div : %d\n" , ret); |
460 | |
461 | ret = devm_snd_soc_register_component(dev, component_driver: &src4xxx_driver, |
462 | dai_drv: src4xxx_dai_driver, ARRAY_SIZE(src4xxx_dai_driver)); |
463 | if (ret == 0) |
464 | dev_info(dev, "src4392 probe ok %d\n" , ret); |
465 | return ret; |
466 | } |
467 | EXPORT_SYMBOL_GPL(src4xxx_probe); |
468 | |
469 | static bool src4xxx_volatile_register(struct device *dev, unsigned int reg) |
470 | { |
471 | switch (reg) { |
472 | case SRC4XXX_RES_00: |
473 | case SRC4XXX_GLOBAL_ITR_STS_02: |
474 | case SRC4XXX_SRC_DIT_STS_0A: |
475 | case SRC4XXX_NON_AUDIO_D_12: |
476 | case SRC4XXX_RVC_STS_13: |
477 | case SRC4XXX_RVC_STS_14: |
478 | case SRC4XXX_RVC_STS_15: |
479 | case SRC4XXX_SUB_CODE_1F: |
480 | case SRC4XXX_SUB_CODE_20: |
481 | case SRC4XXX_SUB_CODE_21: |
482 | case SRC4XXX_SUB_CODE_22: |
483 | case SRC4XXX_SUB_CODE_23: |
484 | case SRC4XXX_SUB_CODE_24: |
485 | case SRC4XXX_SUB_CODE_25: |
486 | case SRC4XXX_SUB_CODE_26: |
487 | case SRC4XXX_SUB_CODE_27: |
488 | case SRC4XXX_SUB_CODE_28: |
489 | case SRC4XXX_PC_PREAMBLE_HI_29: |
490 | case SRC4XXX_PC_PREAMBLE_LO_2A: |
491 | case SRC4XXX_PD_PREAMBLE_HI_2B: |
492 | case SRC4XXX_PC_PREAMBLE_LO_2C: |
493 | case SRC4XXX_IO_RATIO_32: |
494 | case SRC4XXX_IO_RATIO_33: |
495 | return true; |
496 | } |
497 | |
498 | if (reg > SRC4XXX_IO_RATIO_33 && reg < SRC4XXX_PAGE_SEL_7F) |
499 | return true; |
500 | |
501 | return false; |
502 | } |
503 | |
504 | const struct regmap_config src4xxx_regmap_config = { |
505 | .val_bits = 8, |
506 | .reg_bits = 8, |
507 | .max_register = SRC4XXX_IO_RATIO_33, |
508 | |
509 | .reg_defaults = src4xxx_reg_defaults, |
510 | .num_reg_defaults = ARRAY_SIZE(src4xxx_reg_defaults), |
511 | .volatile_reg = src4xxx_volatile_register, |
512 | .cache_type = REGCACHE_RBTREE, |
513 | }; |
514 | EXPORT_SYMBOL_GPL(src4xxx_regmap_config); |
515 | |
516 | MODULE_DESCRIPTION("ASoC SRC4XXX CODEC driver" ); |
517 | MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>" ); |
518 | MODULE_LICENSE("GPL" ); |
519 | |