Blame view

sound/i2c/other/ak4117.c 16.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   *  Routines for control of the AK4117 via 4-wire serial interface
   *  IEC958 (S/PDIF) receiver by Asahi Kasei
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
4
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   *
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
  
  #include <sound/driver.h>
  #include <linux/slab.h>
  #include <linux/delay.h>
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/pcm.h>
  #include <sound/ak4117.h>
  #include <sound/asoundef.h>
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
31
  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
  MODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei");
  MODULE_LICENSE("GPL");
  
  #define AK4117_ADDR			0x00 /* fixed address */
  
  static void snd_ak4117_timer(unsigned long data);
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
38
  static void reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
  {
  	ak4117->write(ak4117->private_data, reg, val);
  	if (reg < sizeof(ak4117->regmap))
  		ak4117->regmap[reg] = val;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
44
  static inline unsigned char reg_read(struct ak4117 *ak4117, unsigned char reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
  {
  	return ak4117->read(ak4117->private_data, reg);
  }
  
  #if 0
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
50
  static void reg_dump(struct ak4117 *ak4117)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
  {
  	int i;
99b359ba1   Takashi Iwai   [ALSA] Add missin...
53
54
  	printk(KERN_DEBUG "AK4117 REG DUMP:
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  	for (i = 0; i < 0x1b; i++)
99b359ba1   Takashi Iwai   [ALSA] Add missin...
56
57
  		printk(KERN_DEBUG "reg[%02x] = %02x (%02x)
  ", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
  }
  #endif
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
60
  static void snd_ak4117_free(struct ak4117 *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
  {
  	del_timer(&chip->timer);
  	kfree(chip);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
65
  static int snd_ak4117_dev_free(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
67
  	struct ak4117 *chip = device->device_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
  	snd_ak4117_free(chip);
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
71
  int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write,
517400cbc   Takashi Iwai   [ALSA] Add some m...
72
  		      const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
74
  	struct ak4117 *chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
  	int err = 0;
  	unsigned char reg;
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
77
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
  		.dev_free =     snd_ak4117_dev_free,
  	};
561b220a4   Takashi Iwai   [ALSA] Replace wi...
80
  	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  	if (chip == NULL)
  		return -ENOMEM;
  	spin_lock_init(&chip->lock);
  	chip->card = card;
  	chip->read = read;
  	chip->write = write;
  	chip->private_data = private_data;
  	init_timer(&chip->timer);
  	chip->timer.data = (unsigned long)chip;
  	chip->timer.function = snd_ak4117_timer;
  
  	for (reg = 0; reg < 5; reg++)
  		chip->regmap[reg] = pgm[reg];
  	snd_ak4117_reinit(chip);
  
  	chip->rcs0 = reg_read(chip, AK4117_REG_RCS0) & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
  	chip->rcs1 = reg_read(chip, AK4117_REG_RCS1);
  	chip->rcs2 = reg_read(chip, AK4117_REG_RCS2);
  
  	if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0)
  		goto __fail;
  
  	if (r_ak4117)
  		*r_ak4117 = chip;
  	return 0;
  
        __fail:
  	snd_ak4117_free(chip);
  	return err < 0 ? err : -EIO;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
111
  void snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
  {
  	if (reg >= 5)
  		return;
  	reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
117
  void snd_ak4117_reinit(struct ak4117 *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  {
  	unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg;
  
  	del_timer(&chip->timer);
  	chip->init = 1;
  	/* bring the chip to reset state and powerdown state */
  	reg_write(chip, AK4117_REG_PWRDN, 0);
  	udelay(200);
  	/* release reset, but leave powerdown */
  	reg_write(chip, AK4117_REG_PWRDN, (old | AK4117_RST) & ~AK4117_PWN);
  	udelay(200);
  	for (reg = 1; reg < 5; reg++)
  		reg_write(chip, reg, chip->regmap[reg]);
  	/* release powerdown, everything is initialized now */
  	reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN);
  	chip->init = 0;
  	chip->timer.expires = 1 + jiffies;
  	add_timer(&chip->timer);
  }
  
  static unsigned int external_rate(unsigned char rcs1)
  {
  	switch (rcs1 & (AK4117_FS0|AK4117_FS1|AK4117_FS2|AK4117_FS3)) {
  	case AK4117_FS_32000HZ: return 32000;
  	case AK4117_FS_44100HZ: return 44100;
  	case AK4117_FS_48000HZ: return 48000;
  	case AK4117_FS_88200HZ: return 88200;
  	case AK4117_FS_96000HZ: return 96000;
  	case AK4117_FS_176400HZ: return 176400;
  	case AK4117_FS_192000HZ: return 192000;
  	default:		return 0;
  	}
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
151
152
  static int snd_ak4117_in_error_info(struct snd_kcontrol *kcontrol,
  				    struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = LONG_MAX;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
160
161
  static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
163
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
172
  	long *ptr;
  
  	spin_lock_irq(&chip->lock);
  	ptr = (long *)(((char *)chip) + kcontrol->private_value);
  	ucontrol->value.integer.value[0] = *ptr;
  	*ptr = 0;
  	spin_unlock_irq(&chip->lock);
  	return 0;
  }
a5ce88909   Takashi Iwai   [ALSA] Clean up w...
173
  #define snd_ak4117_in_bit_info		snd_ctl_boolean_mono_info
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174

97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
175
176
  static int snd_ak4117_in_bit_get(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
178
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
182
183
184
185
  	unsigned char reg = kcontrol->private_value & 0xff;
  	unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
  	unsigned char inv = (kcontrol->private_value >> 31) & 1;
  
  	ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
186
187
  static int snd_ak4117_rx_info(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 1;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
195
196
  static int snd_ak4117_rx_get(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
198
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
  
  	ucontrol->value.integer.value[0] = (chip->regmap[AK4117_REG_IO] & AK4117_IPS) ? 1 : 0;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
203
204
  static int snd_ak4117_rx_put(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
206
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
210
211
212
213
214
215
216
217
  	int change;
  	u8 old_val;
  	
  	spin_lock_irq(&chip->lock);
  	old_val = chip->regmap[AK4117_REG_IO];
  	change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0);
  	if (change)
  		reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0));
  	spin_unlock_irq(&chip->lock);
  	return change;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
218
219
  static int snd_ak4117_rate_info(struct snd_kcontrol *kcontrol,
  				struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 192000;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
227
228
  static int snd_ak4117_rate_get(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
230
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
  
  	ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4117_REG_RCS1));
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
235
  static int snd_ak4117_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
  	uinfo->count = 1;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
241
242
  static int snd_ak4117_spdif_get(struct snd_kcontrol *kcontrol,
  				struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
244
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
  	unsigned i;
  
  	for (i = 0; i < AK4117_REG_RXCSB_SIZE; i++)
  		ucontrol->value.iec958.status[i] = reg_read(chip, AK4117_REG_RXCSB0 + i);
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
251
  static int snd_ak4117_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
  	uinfo->count = 1;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
257
258
  static int snd_ak4117_spdif_mask_get(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
  {
  	memset(ucontrol->value.iec958.status, 0xff, AK4117_REG_RXCSB_SIZE);
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
263
  static int snd_ak4117_spdif_pinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 0xffff;
  	uinfo->count = 4;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
271
272
  static int snd_ak4117_spdif_pget(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
274
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
283
284
  	unsigned short tmp;
  
  	ucontrol->value.integer.value[0] = 0xf8f2;
  	ucontrol->value.integer.value[1] = 0x4e1f;
  	tmp = reg_read(chip, AK4117_REG_Pc0) | (reg_read(chip, AK4117_REG_Pc1) << 8);
  	ucontrol->value.integer.value[2] = tmp;
  	tmp = reg_read(chip, AK4117_REG_Pd0) | (reg_read(chip, AK4117_REG_Pd1) << 8);
  	ucontrol->value.integer.value[3] = tmp;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
285
  static int snd_ak4117_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
  	uinfo->count = AK4117_REG_QSUB_SIZE;
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
291
292
  static int snd_ak4117_spdif_qget(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
294
  	struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
  	unsigned i;
  
  	for (i = 0; i < AK4117_REG_QSUB_SIZE; i++)
  		ucontrol->value.bytes.data[i] = reg_read(chip, AK4117_REG_QSUB_ADDR + i);
  	return 0;
  }
  
  /* Don't forget to change AK4117_CONTROLS define!!! */
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
303
  static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
308
309
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 Parity Errors",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_error_info,
  	.get =		snd_ak4117_in_error_get,
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
310
  	.private_value = offsetof(struct ak4117, parity_errors),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 V-Bit Errors",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_error_info,
  	.get =		snd_ak4117_in_error_get,
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
318
  	.private_value = offsetof(struct ak4117, v_bit_errors),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 C-CRC Errors",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_error_info,
  	.get =		snd_ak4117_in_error_get,
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
326
  	.private_value = offsetof(struct ak4117, ccrc_errors),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
333
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 Q-CRC Errors",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_error_info,
  	.get =		snd_ak4117_in_error_get,
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
334
  	.private_value = offsetof(struct ak4117, qcrc_errors),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 External Rate",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_rate_info,
  	.get =		snd_ak4117_rate_get,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
  	.info =		snd_ak4117_spdif_mask_info,
  	.get =		snd_ak4117_spdif_mask_get,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_spdif_info,
  	.get =		snd_ak4117_spdif_get,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 Preample Capture Default",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_spdif_pinfo,
  	.get =		snd_ak4117_spdif_pget,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 Q-subcode Capture Default",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_spdif_qinfo,
  	.get =		snd_ak4117_spdif_qget,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 Audio",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_bit_info,
  	.get =		snd_ak4117_in_bit_get,
  	.private_value = (1<<31) | (3<<8) | AK4117_REG_RCS0,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 Non-PCM Bitstream",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_bit_info,
  	.get =		snd_ak4117_in_bit_get,
  	.private_value = (5<<8) | AK4117_REG_RCS1,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"IEC958 DTS Bitstream",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  	.info =		snd_ak4117_in_bit_info,
  	.get =		snd_ak4117_in_bit_get,
  	.private_value = (6<<8) | AK4117_REG_RCS1,
  },
  {
  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =		"AK4117 Input Select",
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
  	.info =		snd_ak4117_rx_info,
  	.get =		snd_ak4117_rx_get,
  	.put =		snd_ak4117_rx_put,
  }
  };
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
404
  int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *cap_substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
406
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  	unsigned int idx;
  	int err;
  
  	snd_assert(cap_substream, return -EINVAL);
  	ak4117->substream = cap_substream;
  	for (idx = 0; idx < AK4117_CONTROLS; idx++) {
  		kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117);
  		if (kctl == NULL)
  			return -ENOMEM;
  		kctl->id.device = cap_substream->pcm->device;
  		kctl->id.subdevice = cap_substream->number;
  		err = snd_ctl_add(ak4117->card, kctl);
  		if (err < 0)
  			return err;
  		ak4117->kctls[idx] = kctl;
  	}
  	return 0;
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
425
  int snd_ak4117_external_rate(struct ak4117 *ak4117)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
429
430
431
  {
  	unsigned char rcs1;
  
  	rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
  	return external_rate(rcs1);
  }
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
432
  int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
434
  	struct snd_pcm_runtime *runtime = ak4117->substream ? ak4117->substream->runtime : NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
438
439
440
441
442
443
444
  	unsigned long _flags;
  	int res = 0;
  	unsigned char rcs0, rcs1, rcs2;
  	unsigned char c0, c1;
  
  	rcs1 = reg_read(ak4117, AK4117_REG_RCS1);
  	if (flags & AK4117_CHECK_NO_STAT)
  		goto __rate;
  	rcs0 = reg_read(ak4117, AK4117_REG_RCS0);
  	rcs2 = reg_read(ak4117, AK4117_REG_RCS2);
99b359ba1   Takashi Iwai   [ALSA] Add missin...
445
446
  	// printk(KERN_DEBUG "AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x
  ", rcs0, rcs1, rcs2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  	spin_lock_irqsave(&ak4117->lock, _flags);
  	if (rcs0 & AK4117_PAR)
  		ak4117->parity_errors++;
  	if (rcs0 & AK4117_V)
  		ak4117->v_bit_errors++;
  	if (rcs2 & AK4117_CCRC)
  		ak4117->ccrc_errors++;
  	if (rcs2 & AK4117_QCRC)
  		ak4117->qcrc_errors++;
  	c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
                       (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
  	c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
  	             (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
  	ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
  	ak4117->rcs1 = rcs1;
  	ak4117->rcs2 = rcs2;
  	spin_unlock_irqrestore(&ak4117->lock, _flags);
  
  	if (rcs0 & AK4117_PAR)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id);
  	if (rcs0 & AK4117_V)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[1]->id);
  	if (rcs2 & AK4117_CCRC)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[2]->id);
  	if (rcs2 & AK4117_QCRC)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[3]->id);
  
  	/* rate change */
  	if (c1 & 0x0f)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[4]->id);
  
  	if ((c1 & AK4117_PEM) | (c0 & AK4117_CINT))
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[6]->id);
  	if (c0 & AK4117_QINT)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[8]->id);
  
  	if (c0 & AK4117_AUDION)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[9]->id);
  	if (c1 & AK4117_NPCM)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[10]->id);
  	if (c1 & AK4117_DTSCD)
  		snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[11]->id);
  		
  	if (ak4117->change_callback && (c0 | c1) != 0)
  		ak4117->change_callback(ak4117, c0, c1);
  
        __rate:
  	/* compare rate */
  	res = external_rate(rcs1);
  	if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) {
  		snd_pcm_stream_lock_irqsave(ak4117->substream, _flags);
  		if (snd_pcm_running(ak4117->substream)) {
99b359ba1   Takashi Iwai   [ALSA] Add missin...
499
500
  			// printk(KERN_DEBUG "rate changed (%i <- %i)
  ", runtime->rate, res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
507
508
509
510
511
  			snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING);
  			wake_up(&runtime->sleep);
  			res = 1;
  		}
  		snd_pcm_stream_unlock_irqrestore(ak4117->substream, _flags);
  	}
  	return res;
  }
  
  static void snd_ak4117_timer(unsigned long data)
  {
97f02e05f   Takashi Iwai   [ALSA] Remove xxx...
512
  	struct ak4117 *chip = (struct ak4117 *)data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
518
519
520
521
522
523
524
525
526
  
  	if (chip->init)
  		return;
  	snd_ak4117_check_rate_and_errors(chip, 0);
  	chip->timer.expires = 1 + jiffies;
  	add_timer(&chip->timer);
  }
  
  EXPORT_SYMBOL(snd_ak4117_create);
  EXPORT_SYMBOL(snd_ak4117_reg_write);
  EXPORT_SYMBOL(snd_ak4117_reinit);
  EXPORT_SYMBOL(snd_ak4117_build);
  EXPORT_SYMBOL(snd_ak4117_external_rate);
  EXPORT_SYMBOL(snd_ak4117_check_rate_and_errors);