diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/i2c/other/ak4114.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/i2c/other/ak4114.c')
-rw-r--r-- | sound/i2c/other/ak4114.c | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c new file mode 100644 index 000000000000..f5e6018ea3f4 --- /dev/null +++ b/sound/i2c/other/ak4114.c | |||
@@ -0,0 +1,580 @@ | |||
1 | /* | ||
2 | * Routines for control of the AK4114 via I2C and 4-wire serial interface | ||
3 | * IEC958 (S/PDIF) receiver by Asahi Kasei | ||
4 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
5 | * | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/control.h> | ||
28 | #include <sound/pcm.h> | ||
29 | #include <sound/ak4114.h> | ||
30 | #include <sound/asoundef.h> | ||
31 | |||
32 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
33 | MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | #define AK4114_ADDR 0x00 /* fixed address */ | ||
37 | |||
38 | static void ak4114_stats(void *); | ||
39 | |||
40 | static void reg_write(ak4114_t *ak4114, unsigned char reg, unsigned char val) | ||
41 | { | ||
42 | ak4114->write(ak4114->private_data, reg, val); | ||
43 | if (reg <= AK4114_REG_INT1_MASK) | ||
44 | ak4114->regmap[reg] = val; | ||
45 | else if (reg >= AK4114_REG_RXCSB0 && reg <= AK4114_REG_TXCSB4) | ||
46 | ak4114->txcsb[reg-AK4114_REG_RXCSB0] = val; | ||
47 | } | ||
48 | |||
49 | static inline unsigned char reg_read(ak4114_t *ak4114, unsigned char reg) | ||
50 | { | ||
51 | return ak4114->read(ak4114->private_data, reg); | ||
52 | } | ||
53 | |||
54 | #if 0 | ||
55 | static void reg_dump(ak4114_t *ak4114) | ||
56 | { | ||
57 | int i; | ||
58 | |||
59 | printk("AK4114 REG DUMP:\n"); | ||
60 | for (i = 0; i < 0x20; i++) | ||
61 | printk("reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0); | ||
62 | } | ||
63 | #endif | ||
64 | |||
65 | static void snd_ak4114_free(ak4114_t *chip) | ||
66 | { | ||
67 | chip->init = 1; /* don't schedule new work */ | ||
68 | mb(); | ||
69 | if (chip->workqueue != NULL) { | ||
70 | flush_workqueue(chip->workqueue); | ||
71 | destroy_workqueue(chip->workqueue); | ||
72 | } | ||
73 | kfree(chip); | ||
74 | } | ||
75 | |||
76 | static int snd_ak4114_dev_free(snd_device_t *device) | ||
77 | { | ||
78 | ak4114_t *chip = device->device_data; | ||
79 | snd_ak4114_free(chip); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | int snd_ak4114_create(snd_card_t *card, | ||
84 | ak4114_read_t *read, ak4114_write_t *write, | ||
85 | unsigned char pgm[7], unsigned char txcsb[5], | ||
86 | void *private_data, ak4114_t **r_ak4114) | ||
87 | { | ||
88 | ak4114_t *chip; | ||
89 | int err = 0; | ||
90 | unsigned char reg; | ||
91 | static snd_device_ops_t ops = { | ||
92 | .dev_free = snd_ak4114_dev_free, | ||
93 | }; | ||
94 | |||
95 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
96 | if (chip == NULL) | ||
97 | return -ENOMEM; | ||
98 | spin_lock_init(&chip->lock); | ||
99 | chip->card = card; | ||
100 | chip->read = read; | ||
101 | chip->write = write; | ||
102 | chip->private_data = private_data; | ||
103 | |||
104 | for (reg = 0; reg < 7; reg++) | ||
105 | chip->regmap[reg] = pgm[reg]; | ||
106 | for (reg = 0; reg < 5; reg++) | ||
107 | chip->txcsb[reg] = txcsb[reg]; | ||
108 | |||
109 | chip->workqueue = create_workqueue("snd-ak4114"); | ||
110 | if (chip->workqueue == NULL) { | ||
111 | kfree(chip); | ||
112 | return -ENOMEM; | ||
113 | } | ||
114 | |||
115 | snd_ak4114_reinit(chip); | ||
116 | |||
117 | chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT); | ||
118 | chip->rcs1 = reg_read(chip, AK4114_REG_RCS1); | ||
119 | |||
120 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) | ||
121 | goto __fail; | ||
122 | |||
123 | if (r_ak4114) | ||
124 | *r_ak4114 = chip; | ||
125 | return 0; | ||
126 | |||
127 | __fail: | ||
128 | snd_ak4114_free(chip); | ||
129 | return err < 0 ? err : -EIO; | ||
130 | } | ||
131 | |||
132 | void snd_ak4114_reg_write(ak4114_t *chip, unsigned char reg, unsigned char mask, unsigned char val) | ||
133 | { | ||
134 | if (reg <= AK4114_REG_INT1_MASK) | ||
135 | reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val); | ||
136 | else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4) | ||
137 | reg_write(chip, reg, (chip->txcsb[reg] & ~mask) | val); | ||
138 | } | ||
139 | |||
140 | void snd_ak4114_reinit(ak4114_t *chip) | ||
141 | { | ||
142 | unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg; | ||
143 | |||
144 | chip->init = 1; | ||
145 | mb(); | ||
146 | flush_workqueue(chip->workqueue); | ||
147 | /* bring the chip to reset state and powerdown state */ | ||
148 | reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN)); | ||
149 | udelay(200); | ||
150 | /* release reset, but leave powerdown */ | ||
151 | reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN); | ||
152 | udelay(200); | ||
153 | for (reg = 1; reg < 7; reg++) | ||
154 | reg_write(chip, reg, chip->regmap[reg]); | ||
155 | for (reg = 0; reg < 5; reg++) | ||
156 | reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]); | ||
157 | /* release powerdown, everything is initialized now */ | ||
158 | reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN); | ||
159 | /* bring up statistics / event queing */ | ||
160 | chip->init = 0; | ||
161 | INIT_WORK(&chip->work, ak4114_stats, chip); | ||
162 | queue_delayed_work(chip->workqueue, &chip->work, HZ / 10); | ||
163 | } | ||
164 | |||
165 | static unsigned int external_rate(unsigned char rcs1) | ||
166 | { | ||
167 | switch (rcs1 & (AK4114_FS0|AK4114_FS1|AK4114_FS2|AK4114_FS3)) { | ||
168 | case AK4114_FS_32000HZ: return 32000; | ||
169 | case AK4114_FS_44100HZ: return 44100; | ||
170 | case AK4114_FS_48000HZ: return 48000; | ||
171 | case AK4114_FS_88200HZ: return 88200; | ||
172 | case AK4114_FS_96000HZ: return 96000; | ||
173 | case AK4114_FS_176400HZ: return 176400; | ||
174 | case AK4114_FS_192000HZ: return 192000; | ||
175 | default: return 0; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | static int snd_ak4114_in_error_info(snd_kcontrol_t *kcontrol, | ||
180 | snd_ctl_elem_info_t *uinfo) | ||
181 | { | ||
182 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
183 | uinfo->count = 1; | ||
184 | uinfo->value.integer.min = 0; | ||
185 | uinfo->value.integer.max = LONG_MAX; | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int snd_ak4114_in_error_get(snd_kcontrol_t *kcontrol, | ||
190 | snd_ctl_elem_value_t *ucontrol) | ||
191 | { | ||
192 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
193 | long *ptr; | ||
194 | |||
195 | spin_lock_irq(&chip->lock); | ||
196 | ptr = (long *)(((char *)chip) + kcontrol->private_value); | ||
197 | ucontrol->value.integer.value[0] = *ptr; | ||
198 | *ptr = 0; | ||
199 | spin_unlock_irq(&chip->lock); | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int snd_ak4114_in_bit_info(snd_kcontrol_t *kcontrol, | ||
204 | snd_ctl_elem_info_t *uinfo) | ||
205 | { | ||
206 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
207 | uinfo->count = 1; | ||
208 | uinfo->value.integer.min = 0; | ||
209 | uinfo->value.integer.max = 1; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int snd_ak4114_in_bit_get(snd_kcontrol_t *kcontrol, | ||
214 | snd_ctl_elem_value_t *ucontrol) | ||
215 | { | ||
216 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
217 | unsigned char reg = kcontrol->private_value & 0xff; | ||
218 | unsigned char bit = (kcontrol->private_value >> 8) & 0xff; | ||
219 | unsigned char inv = (kcontrol->private_value >> 31) & 1; | ||
220 | |||
221 | ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int snd_ak4114_rate_info(snd_kcontrol_t *kcontrol, | ||
226 | snd_ctl_elem_info_t *uinfo) | ||
227 | { | ||
228 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
229 | uinfo->count = 1; | ||
230 | uinfo->value.integer.min = 0; | ||
231 | uinfo->value.integer.max = 192000; | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int snd_ak4114_rate_get(snd_kcontrol_t *kcontrol, | ||
236 | snd_ctl_elem_value_t *ucontrol) | ||
237 | { | ||
238 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
239 | |||
240 | ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4114_REG_RCS1)); | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int snd_ak4114_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
245 | { | ||
246 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
247 | uinfo->count = 1; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int snd_ak4114_spdif_get(snd_kcontrol_t * kcontrol, | ||
252 | snd_ctl_elem_value_t * ucontrol) | ||
253 | { | ||
254 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
255 | unsigned i; | ||
256 | |||
257 | for (i = 0; i < AK4114_REG_RXCSB_SIZE; i++) | ||
258 | ucontrol->value.iec958.status[i] = reg_read(chip, AK4114_REG_RXCSB0 + i); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int snd_ak4114_spdif_playback_get(snd_kcontrol_t * kcontrol, | ||
263 | snd_ctl_elem_value_t * ucontrol) | ||
264 | { | ||
265 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
266 | unsigned i; | ||
267 | |||
268 | for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++) | ||
269 | ucontrol->value.iec958.status[i] = chip->txcsb[i]; | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int snd_ak4114_spdif_playback_put(snd_kcontrol_t * kcontrol, | ||
274 | snd_ctl_elem_value_t * ucontrol) | ||
275 | { | ||
276 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
277 | unsigned i; | ||
278 | |||
279 | for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++) | ||
280 | reg_write(chip, AK4114_REG_TXCSB0 + i, ucontrol->value.iec958.status[i]); | ||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int snd_ak4114_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
285 | { | ||
286 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
287 | uinfo->count = 1; | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int snd_ak4114_spdif_mask_get(snd_kcontrol_t * kcontrol, | ||
292 | snd_ctl_elem_value_t * ucontrol) | ||
293 | { | ||
294 | memset(ucontrol->value.iec958.status, 0xff, AK4114_REG_RXCSB_SIZE); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int snd_ak4114_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
299 | { | ||
300 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
301 | uinfo->value.integer.min = 0; | ||
302 | uinfo->value.integer.max = 0xffff; | ||
303 | uinfo->count = 4; | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static int snd_ak4114_spdif_pget(snd_kcontrol_t * kcontrol, | ||
308 | snd_ctl_elem_value_t * ucontrol) | ||
309 | { | ||
310 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
311 | unsigned short tmp; | ||
312 | |||
313 | ucontrol->value.integer.value[0] = 0xf8f2; | ||
314 | ucontrol->value.integer.value[1] = 0x4e1f; | ||
315 | tmp = reg_read(chip, AK4114_REG_Pc0) | (reg_read(chip, AK4114_REG_Pc1) << 8); | ||
316 | ucontrol->value.integer.value[2] = tmp; | ||
317 | tmp = reg_read(chip, AK4114_REG_Pd0) | (reg_read(chip, AK4114_REG_Pd1) << 8); | ||
318 | ucontrol->value.integer.value[3] = tmp; | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int snd_ak4114_spdif_qinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
323 | { | ||
324 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; | ||
325 | uinfo->count = AK4114_REG_QSUB_SIZE; | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int snd_ak4114_spdif_qget(snd_kcontrol_t * kcontrol, | ||
330 | snd_ctl_elem_value_t * ucontrol) | ||
331 | { | ||
332 | ak4114_t *chip = snd_kcontrol_chip(kcontrol); | ||
333 | unsigned i; | ||
334 | |||
335 | for (i = 0; i < AK4114_REG_QSUB_SIZE; i++) | ||
336 | ucontrol->value.bytes.data[i] = reg_read(chip, AK4114_REG_QSUB_ADDR + i); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* Don't forget to change AK4114_CONTROLS define!!! */ | ||
341 | static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = { | ||
342 | { | ||
343 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
344 | .name = "IEC958 Parity Errors", | ||
345 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
346 | .info = snd_ak4114_in_error_info, | ||
347 | .get = snd_ak4114_in_error_get, | ||
348 | .private_value = offsetof(ak4114_t, parity_errors), | ||
349 | }, | ||
350 | { | ||
351 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
352 | .name = "IEC958 V-Bit Errors", | ||
353 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
354 | .info = snd_ak4114_in_error_info, | ||
355 | .get = snd_ak4114_in_error_get, | ||
356 | .private_value = offsetof(ak4114_t, v_bit_errors), | ||
357 | }, | ||
358 | { | ||
359 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
360 | .name = "IEC958 C-CRC Errors", | ||
361 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
362 | .info = snd_ak4114_in_error_info, | ||
363 | .get = snd_ak4114_in_error_get, | ||
364 | .private_value = offsetof(ak4114_t, ccrc_errors), | ||
365 | }, | ||
366 | { | ||
367 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
368 | .name = "IEC958 Q-CRC Errors", | ||
369 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
370 | .info = snd_ak4114_in_error_info, | ||
371 | .get = snd_ak4114_in_error_get, | ||
372 | .private_value = offsetof(ak4114_t, qcrc_errors), | ||
373 | }, | ||
374 | { | ||
375 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
376 | .name = "IEC958 External Rate", | ||
377 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
378 | .info = snd_ak4114_rate_info, | ||
379 | .get = snd_ak4114_rate_get, | ||
380 | }, | ||
381 | { | ||
382 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
383 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), | ||
384 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
385 | .info = snd_ak4114_spdif_mask_info, | ||
386 | .get = snd_ak4114_spdif_mask_get, | ||
387 | }, | ||
388 | { | ||
389 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
390 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | ||
391 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
392 | .info = snd_ak4114_spdif_info, | ||
393 | .get = snd_ak4114_spdif_playback_get, | ||
394 | .put = snd_ak4114_spdif_playback_put, | ||
395 | }, | ||
396 | { | ||
397 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
398 | .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK), | ||
399 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
400 | .info = snd_ak4114_spdif_mask_info, | ||
401 | .get = snd_ak4114_spdif_mask_get, | ||
402 | }, | ||
403 | { | ||
404 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
405 | .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), | ||
406 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
407 | .info = snd_ak4114_spdif_info, | ||
408 | .get = snd_ak4114_spdif_get, | ||
409 | }, | ||
410 | { | ||
411 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
412 | .name = "IEC958 Preample Capture Default", | ||
413 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
414 | .info = snd_ak4114_spdif_pinfo, | ||
415 | .get = snd_ak4114_spdif_pget, | ||
416 | }, | ||
417 | { | ||
418 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
419 | .name = "IEC958 Q-subcode Capture Default", | ||
420 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
421 | .info = snd_ak4114_spdif_qinfo, | ||
422 | .get = snd_ak4114_spdif_qget, | ||
423 | }, | ||
424 | { | ||
425 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
426 | .name = "IEC958 Audio", | ||
427 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
428 | .info = snd_ak4114_in_bit_info, | ||
429 | .get = snd_ak4114_in_bit_get, | ||
430 | .private_value = (1<<31) | (1<<8) | AK4114_REG_RCS0, | ||
431 | }, | ||
432 | { | ||
433 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
434 | .name = "IEC958 Non-PCM Bitstream", | ||
435 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
436 | .info = snd_ak4114_in_bit_info, | ||
437 | .get = snd_ak4114_in_bit_get, | ||
438 | .private_value = (6<<8) | AK4114_REG_RCS1, | ||
439 | }, | ||
440 | { | ||
441 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
442 | .name = "IEC958 DTS Bitstream", | ||
443 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | ||
444 | .info = snd_ak4114_in_bit_info, | ||
445 | .get = snd_ak4114_in_bit_get, | ||
446 | .private_value = (3<<8) | AK4114_REG_RCS1, | ||
447 | } | ||
448 | }; | ||
449 | |||
450 | int snd_ak4114_build(ak4114_t *ak4114, | ||
451 | snd_pcm_substream_t *ply_substream, | ||
452 | snd_pcm_substream_t *cap_substream) | ||
453 | { | ||
454 | snd_kcontrol_t *kctl; | ||
455 | unsigned int idx; | ||
456 | int err; | ||
457 | |||
458 | snd_assert(cap_substream, return -EINVAL); | ||
459 | ak4114->playback_substream = ply_substream; | ||
460 | ak4114->capture_substream = cap_substream; | ||
461 | for (idx = 0; idx < AK4114_CONTROLS; idx++) { | ||
462 | kctl = snd_ctl_new1(&snd_ak4114_iec958_controls[idx], ak4114); | ||
463 | if (kctl == NULL) | ||
464 | return -ENOMEM; | ||
465 | if (!strstr(kctl->id.name, "Playback")) { | ||
466 | if (ply_substream == NULL) { | ||
467 | snd_ctl_free_one(kctl); | ||
468 | ak4114->kctls[idx] = NULL; | ||
469 | continue; | ||
470 | } | ||
471 | kctl->id.device = ply_substream->pcm->device; | ||
472 | kctl->id.subdevice = ply_substream->number; | ||
473 | } else { | ||
474 | kctl->id.device = cap_substream->pcm->device; | ||
475 | kctl->id.subdevice = cap_substream->number; | ||
476 | } | ||
477 | err = snd_ctl_add(ak4114->card, kctl); | ||
478 | if (err < 0) | ||
479 | return err; | ||
480 | ak4114->kctls[idx] = kctl; | ||
481 | } | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | int snd_ak4114_external_rate(ak4114_t *ak4114) | ||
486 | { | ||
487 | unsigned char rcs1; | ||
488 | |||
489 | rcs1 = reg_read(ak4114, AK4114_REG_RCS1); | ||
490 | return external_rate(rcs1); | ||
491 | } | ||
492 | |||
493 | int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags) | ||
494 | { | ||
495 | snd_pcm_runtime_t *runtime = ak4114->capture_substream ? ak4114->capture_substream->runtime : NULL; | ||
496 | unsigned long _flags; | ||
497 | int res = 0; | ||
498 | unsigned char rcs0, rcs1; | ||
499 | unsigned char c0, c1; | ||
500 | |||
501 | rcs1 = reg_read(ak4114, AK4114_REG_RCS1); | ||
502 | if (flags & AK4114_CHECK_NO_STAT) | ||
503 | goto __rate; | ||
504 | rcs0 = reg_read(ak4114, AK4114_REG_RCS0); | ||
505 | spin_lock_irqsave(&ak4114->lock, _flags); | ||
506 | if (rcs0 & AK4114_PAR) | ||
507 | ak4114->parity_errors++; | ||
508 | if (rcs1 & AK4114_V) | ||
509 | ak4114->v_bit_errors++; | ||
510 | if (rcs1 & AK4114_CCRC) | ||
511 | ak4114->ccrc_errors++; | ||
512 | if (rcs1 & AK4114_QCRC) | ||
513 | ak4114->qcrc_errors++; | ||
514 | c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^ | ||
515 | (rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)); | ||
516 | c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0); | ||
517 | ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT); | ||
518 | ak4114->rcs1 = rcs1; | ||
519 | spin_unlock_irqrestore(&ak4114->lock, _flags); | ||
520 | |||
521 | if (rcs0 & AK4114_PAR) | ||
522 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[0]->id); | ||
523 | if (rcs0 & AK4114_V) | ||
524 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[1]->id); | ||
525 | if (rcs1 & AK4114_CCRC) | ||
526 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[2]->id); | ||
527 | if (rcs1 & AK4114_QCRC) | ||
528 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[3]->id); | ||
529 | |||
530 | /* rate change */ | ||
531 | if (c1 & 0xf0) | ||
532 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[4]->id); | ||
533 | |||
534 | if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT)) | ||
535 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[9]->id); | ||
536 | if (c0 & AK4114_QINT) | ||
537 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[10]->id); | ||
538 | |||
539 | if (c0 & AK4114_AUDION) | ||
540 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[11]->id); | ||
541 | if (c0 & AK4114_AUTO) | ||
542 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[12]->id); | ||
543 | if (c0 & AK4114_DTSCD) | ||
544 | snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[13]->id); | ||
545 | |||
546 | if (ak4114->change_callback && (c0 | c1) != 0) | ||
547 | ak4114->change_callback(ak4114, c0, c1); | ||
548 | |||
549 | __rate: | ||
550 | /* compare rate */ | ||
551 | res = external_rate(rcs1); | ||
552 | if (!(flags & AK4114_CHECK_NO_RATE) && runtime && runtime->rate != res) { | ||
553 | snd_pcm_stream_lock_irqsave(ak4114->capture_substream, _flags); | ||
554 | if (snd_pcm_running(ak4114->capture_substream)) { | ||
555 | // printk("rate changed (%i <- %i)\n", runtime->rate, res); | ||
556 | snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING); | ||
557 | wake_up(&runtime->sleep); | ||
558 | res = 1; | ||
559 | } | ||
560 | snd_pcm_stream_unlock_irqrestore(ak4114->capture_substream, _flags); | ||
561 | } | ||
562 | return res; | ||
563 | } | ||
564 | |||
565 | static void ak4114_stats(void *data) | ||
566 | { | ||
567 | ak4114_t *chip = (ak4114_t *)data; | ||
568 | |||
569 | if (chip->init) | ||
570 | return; | ||
571 | snd_ak4114_check_rate_and_errors(chip, 0); | ||
572 | queue_delayed_work(chip->workqueue, &chip->work, HZ / 10); | ||
573 | } | ||
574 | |||
575 | EXPORT_SYMBOL(snd_ak4114_create); | ||
576 | EXPORT_SYMBOL(snd_ak4114_reg_write); | ||
577 | EXPORT_SYMBOL(snd_ak4114_reinit); | ||
578 | EXPORT_SYMBOL(snd_ak4114_build); | ||
579 | EXPORT_SYMBOL(snd_ak4114_external_rate); | ||
580 | EXPORT_SYMBOL(snd_ak4114_check_rate_and_errors); | ||