1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Routines for control of the AK4117 via 4-wire serial interface
4 * IEC958 (S/PDIF) receiver by Asahi Kasei
5 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
6 */
7
8#include <linux/slab.h>
9#include <linux/delay.h>
10#include <linux/module.h>
11#include <sound/core.h>
12#include <sound/control.h>
13#include <sound/pcm.h>
14#include <sound/ak4117.h>
15#include <sound/asoundef.h>
16
17MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
18MODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei");
19MODULE_LICENSE("GPL");
20
21#define AK4117_ADDR 0x00 /* fixed address */
22
23static void snd_ak4117_timer(struct timer_list *t);
24
25static void reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char val)
26{
27 ak4117->write(ak4117->private_data, reg, val);
28 if (reg < sizeof(ak4117->regmap))
29 ak4117->regmap[reg] = val;
30}
31
32static inline unsigned char reg_read(struct ak4117 *ak4117, unsigned char reg)
33{
34 return ak4117->read(ak4117->private_data, reg);
35}
36
37static void snd_ak4117_free(struct ak4117 *chip)
38{
39 timer_shutdown_sync(timer: &chip->timer);
40 kfree(objp: chip);
41}
42
43static int snd_ak4117_dev_free(struct snd_device *device)
44{
45 struct ak4117 *chip = device->device_data;
46 snd_ak4117_free(chip);
47 return 0;
48}
49
50int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write,
51 const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
52{
53 struct ak4117 *chip;
54 int err = 0;
55 unsigned char reg;
56 static const struct snd_device_ops ops = {
57 .dev_free = snd_ak4117_dev_free,
58 };
59
60 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
61 if (chip == NULL)
62 return -ENOMEM;
63 spin_lock_init(&chip->lock);
64 chip->card = card;
65 chip->read = read;
66 chip->write = write;
67 chip->private_data = private_data;
68 timer_setup(&chip->timer, snd_ak4117_timer, 0);
69
70 for (reg = 0; reg < 5; reg++)
71 chip->regmap[reg] = pgm[reg];
72 snd_ak4117_reinit(ak4117: chip);
73
74 chip->rcs0 = reg_read(ak4117: chip, AK4117_REG_RCS0) & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
75 chip->rcs1 = reg_read(ak4117: chip, AK4117_REG_RCS1);
76 chip->rcs2 = reg_read(ak4117: chip, AK4117_REG_RCS2);
77
78 err = snd_device_new(card, type: SNDRV_DEV_CODEC, device_data: chip, ops: &ops);
79 if (err < 0)
80 goto __fail;
81
82 if (r_ak4117)
83 *r_ak4117 = chip;
84 return 0;
85
86 __fail:
87 snd_ak4117_free(chip);
88 return err;
89}
90
91void snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)
92{
93 if (reg >= 5)
94 return;
95 reg_write(ak4117: chip, reg, val: (chip->regmap[reg] & ~mask) | val);
96}
97
98void snd_ak4117_reinit(struct ak4117 *chip)
99{
100 unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg;
101
102 timer_delete(timer: &chip->timer);
103 chip->init = 1;
104 /* bring the chip to reset state and powerdown state */
105 reg_write(ak4117: chip, AK4117_REG_PWRDN, val: 0);
106 udelay(usec: 200);
107 /* release reset, but leave powerdown */
108 reg_write(ak4117: chip, AK4117_REG_PWRDN, val: (old | AK4117_RST) & ~AK4117_PWN);
109 udelay(usec: 200);
110 for (reg = 1; reg < 5; reg++)
111 reg_write(ak4117: chip, reg, val: chip->regmap[reg]);
112 /* release powerdown, everything is initialized now */
113 reg_write(ak4117: chip, AK4117_REG_PWRDN, val: old | AK4117_RST | AK4117_PWN);
114 chip->init = 0;
115 mod_timer(timer: &chip->timer, expires: 1 + jiffies);
116}
117
118static unsigned int external_rate(unsigned char rcs1)
119{
120 switch (rcs1 & (AK4117_FS0|AK4117_FS1|AK4117_FS2|AK4117_FS3)) {
121 case AK4117_FS_32000HZ: return 32000;
122 case AK4117_FS_44100HZ: return 44100;
123 case AK4117_FS_48000HZ: return 48000;
124 case AK4117_FS_88200HZ: return 88200;
125 case AK4117_FS_96000HZ: return 96000;
126 case AK4117_FS_176400HZ: return 176400;
127 case AK4117_FS_192000HZ: return 192000;
128 default: return 0;
129 }
130}
131
132static int snd_ak4117_in_error_info(struct snd_kcontrol *kcontrol,
133 struct snd_ctl_elem_info *uinfo)
134{
135 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
136 uinfo->count = 1;
137 uinfo->value.integer.min = 0;
138 uinfo->value.integer.max = LONG_MAX;
139 return 0;
140}
141
142static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
143 struct snd_ctl_elem_value *ucontrol)
144{
145 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
146
147 guard(spinlock_irq)(l: &chip->lock);
148 ucontrol->value.integer.value[0] =
149 chip->errors[kcontrol->private_value];
150 chip->errors[kcontrol->private_value] = 0;
151 return 0;
152}
153
154#define snd_ak4117_in_bit_info snd_ctl_boolean_mono_info
155
156static int snd_ak4117_in_bit_get(struct snd_kcontrol *kcontrol,
157 struct snd_ctl_elem_value *ucontrol)
158{
159 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
160 unsigned char reg = kcontrol->private_value & 0xff;
161 unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
162 unsigned char inv = (kcontrol->private_value >> 31) & 1;
163
164 ucontrol->value.integer.value[0] = ((reg_read(ak4117: chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
165 return 0;
166}
167
168static int snd_ak4117_rx_info(struct snd_kcontrol *kcontrol,
169 struct snd_ctl_elem_info *uinfo)
170{
171 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
172 uinfo->count = 1;
173 uinfo->value.integer.min = 0;
174 uinfo->value.integer.max = 1;
175 return 0;
176}
177
178static int snd_ak4117_rx_get(struct snd_kcontrol *kcontrol,
179 struct snd_ctl_elem_value *ucontrol)
180{
181 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
182
183 ucontrol->value.integer.value[0] = (chip->regmap[AK4117_REG_IO] & AK4117_IPS) ? 1 : 0;
184 return 0;
185}
186
187static int snd_ak4117_rx_put(struct snd_kcontrol *kcontrol,
188 struct snd_ctl_elem_value *ucontrol)
189{
190 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
191 int change;
192 u8 old_val;
193
194 guard(spinlock_irq)(l: &chip->lock);
195 old_val = chip->regmap[AK4117_REG_IO];
196 change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0);
197 if (change)
198 reg_write(ak4117: chip, AK4117_REG_IO, val: (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0));
199 return change;
200}
201
202static int snd_ak4117_rate_info(struct snd_kcontrol *kcontrol,
203 struct snd_ctl_elem_info *uinfo)
204{
205 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
206 uinfo->count = 1;
207 uinfo->value.integer.min = 0;
208 uinfo->value.integer.max = 192000;
209 return 0;
210}
211
212static int snd_ak4117_rate_get(struct snd_kcontrol *kcontrol,
213 struct snd_ctl_elem_value *ucontrol)
214{
215 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
216
217 ucontrol->value.integer.value[0] = external_rate(rcs1: reg_read(ak4117: chip, AK4117_REG_RCS1));
218 return 0;
219}
220
221static int snd_ak4117_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
222{
223 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
224 uinfo->count = 1;
225 return 0;
226}
227
228static int snd_ak4117_spdif_get(struct snd_kcontrol *kcontrol,
229 struct snd_ctl_elem_value *ucontrol)
230{
231 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
232 unsigned i;
233
234 for (i = 0; i < AK4117_REG_RXCSB_SIZE; i++)
235 ucontrol->value.iec958.status[i] = reg_read(ak4117: chip, AK4117_REG_RXCSB0 + i);
236 return 0;
237}
238
239static int snd_ak4117_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
240{
241 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
242 uinfo->count = 1;
243 return 0;
244}
245
246static int snd_ak4117_spdif_mask_get(struct snd_kcontrol *kcontrol,
247 struct snd_ctl_elem_value *ucontrol)
248{
249 memset(ucontrol->value.iec958.status, 0xff, AK4117_REG_RXCSB_SIZE);
250 return 0;
251}
252
253static int snd_ak4117_spdif_pinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
254{
255 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
256 uinfo->value.integer.min = 0;
257 uinfo->value.integer.max = 0xffff;
258 uinfo->count = 4;
259 return 0;
260}
261
262static int snd_ak4117_spdif_pget(struct snd_kcontrol *kcontrol,
263 struct snd_ctl_elem_value *ucontrol)
264{
265 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
266 unsigned short tmp;
267
268 ucontrol->value.integer.value[0] = 0xf8f2;
269 ucontrol->value.integer.value[1] = 0x4e1f;
270 tmp = reg_read(ak4117: chip, AK4117_REG_Pc0) | (reg_read(ak4117: chip, AK4117_REG_Pc1) << 8);
271 ucontrol->value.integer.value[2] = tmp;
272 tmp = reg_read(ak4117: chip, AK4117_REG_Pd0) | (reg_read(ak4117: chip, AK4117_REG_Pd1) << 8);
273 ucontrol->value.integer.value[3] = tmp;
274 return 0;
275}
276
277static int snd_ak4117_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
278{
279 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
280 uinfo->count = AK4117_REG_QSUB_SIZE;
281 return 0;
282}
283
284static int snd_ak4117_spdif_qget(struct snd_kcontrol *kcontrol,
285 struct snd_ctl_elem_value *ucontrol)
286{
287 struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
288 unsigned i;
289
290 for (i = 0; i < AK4117_REG_QSUB_SIZE; i++)
291 ucontrol->value.bytes.data[i] = reg_read(ak4117: chip, AK4117_REG_QSUB_ADDR + i);
292 return 0;
293}
294
295/* Don't forget to change AK4117_CONTROLS define!!! */
296static const struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
297{
298 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
299 .name = "IEC958 Parity Errors",
300 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
301 .info = snd_ak4117_in_error_info,
302 .get = snd_ak4117_in_error_get,
303 .private_value = AK4117_PARITY_ERRORS,
304},
305{
306 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
307 .name = "IEC958 V-Bit Errors",
308 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
309 .info = snd_ak4117_in_error_info,
310 .get = snd_ak4117_in_error_get,
311 .private_value = AK4117_V_BIT_ERRORS,
312},
313{
314 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
315 .name = "IEC958 C-CRC Errors",
316 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
317 .info = snd_ak4117_in_error_info,
318 .get = snd_ak4117_in_error_get,
319 .private_value = AK4117_CCRC_ERRORS,
320},
321{
322 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
323 .name = "IEC958 Q-CRC Errors",
324 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
325 .info = snd_ak4117_in_error_info,
326 .get = snd_ak4117_in_error_get,
327 .private_value = AK4117_QCRC_ERRORS,
328},
329{
330 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
331 .name = "IEC958 External Rate",
332 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
333 .info = snd_ak4117_rate_info,
334 .get = snd_ak4117_rate_get,
335},
336{
337 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
338 .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
339 .access = SNDRV_CTL_ELEM_ACCESS_READ,
340 .info = snd_ak4117_spdif_mask_info,
341 .get = snd_ak4117_spdif_mask_get,
342},
343{
344 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
345 .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
346 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
347 .info = snd_ak4117_spdif_info,
348 .get = snd_ak4117_spdif_get,
349},
350{
351 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
352 .name = "IEC958 Preamble Capture Default",
353 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
354 .info = snd_ak4117_spdif_pinfo,
355 .get = snd_ak4117_spdif_pget,
356},
357{
358 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
359 .name = "IEC958 Q-subcode Capture Default",
360 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
361 .info = snd_ak4117_spdif_qinfo,
362 .get = snd_ak4117_spdif_qget,
363},
364{
365 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
366 .name = "IEC958 Audio",
367 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
368 .info = snd_ak4117_in_bit_info,
369 .get = snd_ak4117_in_bit_get,
370 .private_value = (1<<31) | (3<<8) | AK4117_REG_RCS0,
371},
372{
373 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
374 .name = "IEC958 Non-PCM Bitstream",
375 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
376 .info = snd_ak4117_in_bit_info,
377 .get = snd_ak4117_in_bit_get,
378 .private_value = (5<<8) | AK4117_REG_RCS1,
379},
380{
381 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
382 .name = "IEC958 DTS Bitstream",
383 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
384 .info = snd_ak4117_in_bit_info,
385 .get = snd_ak4117_in_bit_get,
386 .private_value = (6<<8) | AK4117_REG_RCS1,
387},
388{
389 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
390 .name = "AK4117 Input Select",
391 .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
392 .info = snd_ak4117_rx_info,
393 .get = snd_ak4117_rx_get,
394 .put = snd_ak4117_rx_put,
395}
396};
397
398int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *cap_substream)
399{
400 struct snd_kcontrol *kctl;
401 unsigned int idx;
402 int err;
403
404 if (snd_BUG_ON(!cap_substream))
405 return -EINVAL;
406 ak4117->substream = cap_substream;
407 for (idx = 0; idx < AK4117_CONTROLS; idx++) {
408 kctl = snd_ctl_new1(kcontrolnew: &snd_ak4117_iec958_controls[idx], private_data: ak4117);
409 if (kctl == NULL)
410 return -ENOMEM;
411 kctl->id.device = cap_substream->pcm->device;
412 kctl->id.subdevice = cap_substream->number;
413 err = snd_ctl_add(card: ak4117->card, kcontrol: kctl);
414 if (err < 0)
415 return err;
416 ak4117->kctls[idx] = kctl;
417 }
418 return 0;
419}
420
421int snd_ak4117_external_rate(struct ak4117 *ak4117)
422{
423 unsigned char rcs1;
424
425 rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
426 return external_rate(rcs1);
427}
428
429int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
430{
431 struct snd_pcm_runtime *runtime = ak4117->substream ? ak4117->substream->runtime : NULL;
432 unsigned long _flags;
433 int res = 0;
434 unsigned char rcs0, rcs1, rcs2;
435 unsigned char c0, c1;
436
437 rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
438 if (flags & AK4117_CHECK_NO_STAT)
439 goto __rate;
440 rcs0 = reg_read(ak4117, AK4117_REG_RCS0);
441 rcs2 = reg_read(ak4117, AK4117_REG_RCS2);
442 scoped_guard(spinlock_irqsave, &ak4117->lock) {
443 if (rcs0 & AK4117_PAR)
444 ak4117->errors[AK4117_PARITY_ERRORS]++;
445 if (rcs0 & AK4117_V)
446 ak4117->errors[AK4117_V_BIT_ERRORS]++;
447 if (rcs2 & AK4117_CCRC)
448 ak4117->errors[AK4117_CCRC_ERRORS]++;
449 if (rcs2 & AK4117_QCRC)
450 ak4117->errors[AK4117_QCRC_ERRORS]++;
451 c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
452 (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
453 c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
454 (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
455 ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
456 ak4117->rcs1 = rcs1;
457 ak4117->rcs2 = rcs2;
458 }
459
460 if (rcs0 & AK4117_PAR)
461 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[0]->id);
462 if (rcs0 & AK4117_V)
463 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[1]->id);
464 if (rcs2 & AK4117_CCRC)
465 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[2]->id);
466 if (rcs2 & AK4117_QCRC)
467 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[3]->id);
468
469 /* rate change */
470 if (c1 & 0x0f)
471 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[4]->id);
472
473 if ((c1 & AK4117_PEM) | (c0 & AK4117_CINT))
474 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[6]->id);
475 if (c0 & AK4117_QINT)
476 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[8]->id);
477
478 if (c0 & AK4117_AUDION)
479 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[9]->id);
480 if (c1 & AK4117_NPCM)
481 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[10]->id);
482 if (c1 & AK4117_DTSCD)
483 snd_ctl_notify(card: ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, id: &ak4117->kctls[11]->id);
484
485 if (ak4117->change_callback && (c0 | c1) != 0)
486 ak4117->change_callback(ak4117, c0, c1);
487
488 __rate:
489 /* compare rate */
490 res = external_rate(rcs1);
491 if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) {
492 snd_pcm_stream_lock_irqsave(ak4117->substream, _flags);
493 if (snd_pcm_running(substream: ak4117->substream)) {
494 snd_pcm_stop(substream: ak4117->substream, SNDRV_PCM_STATE_DRAINING);
495 wake_up(&runtime->sleep);
496 res = 1;
497 }
498 snd_pcm_stream_unlock_irqrestore(substream: ak4117->substream, flags: _flags);
499 }
500 return res;
501}
502
503static void snd_ak4117_timer(struct timer_list *t)
504{
505 struct ak4117 *chip = timer_container_of(chip, t, timer);
506
507 if (chip->init)
508 return;
509 snd_ak4117_check_rate_and_errors(ak4117: chip, flags: 0);
510 mod_timer(timer: &chip->timer, expires: 1 + jiffies);
511}
512
513EXPORT_SYMBOL(snd_ak4117_create);
514EXPORT_SYMBOL(snd_ak4117_reg_write);
515EXPORT_SYMBOL(snd_ak4117_reinit);
516EXPORT_SYMBOL(snd_ak4117_build);
517EXPORT_SYMBOL(snd_ak4117_external_rate);
518EXPORT_SYMBOL(snd_ak4117_check_rate_and_errors);
519

source code of linux/sound/i2c/other/ak4117.c