xref: /openbmc/linux/sound/pci/ice1712/se.c (revision e2ad626f)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
4  *
5  *   Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI
6  *
7  *	Copyright (c) 2007 Shin-ya Okada  sh_okada(at)d4.dion.ne.jp
8  *                                        (at) -> @
9  */
10 
11 #include <linux/delay.h>
12 #include <linux/interrupt.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #include <sound/core.h>
16 #include <sound/tlv.h>
17 
18 #include "ice1712.h"
19 #include "envy24ht.h"
20 #include "se.h"
21 
22 struct se_spec {
23 	struct {
24 		unsigned char ch1, ch2;
25 	} vol[8];
26 };
27 
28 /****************************************************************************/
29 /*  ONKYO WAVIO SE-200PCI                                                   */
30 /****************************************************************************/
31 /*
32  *  system configuration ICE_EEP2_SYSCONF=0x4b
33  *    XIN1 49.152MHz
34  *    not have UART
35  *    one stereo ADC and a S/PDIF receiver connected
36  *    four stereo DACs connected
37  *
38  *  AC-Link configuration ICE_EEP2_ACLINK=0x80
39  *    use I2C, not use AC97
40  *
41  *  I2S converters feature ICE_EEP2_I2S=0x78
42  *    I2S codec has no volume/mute control feature
43  *    I2S codec supports 96KHz and 192KHz
44  *    I2S codec 24bits
45  *
46  *  S/PDIF configuration ICE_EEP2_SPDIF=0xc3
47  *    Enable integrated S/PDIF transmitter
48  *    internal S/PDIF out implemented
49  *    S/PDIF is stereo
50  *    External S/PDIF out implemented
51  *
52  *
53  * ** connected chips **
54  *
55  *  WM8740
56  *      A 2ch-DAC of main outputs.
57  *      It setuped as I2S mode by wire, so no way to setup from software.
58  *      The sample-rate are automatically changed.
59  *          ML/I2S (28pin) --------+
60  *          MC/DM1 (27pin) -- 5V   |
61  *          MD/DM0 (26pin) -- GND  |
62  *          MUTEB  (25pin) -- NC   |
63  *          MODE   (24pin) -- GND  |
64  *          CSBIW  (23pin) --------+
65  *                                 |
66  *          RSTB   (22pin) --R(1K)-+
67  *      Probably it reduce the noise from the control line.
68  *
69  *  WM8766
70  *      A 6ch-DAC for surrounds.
71  *      It's control wire was connected to GPIOxx (3-wire serial interface)
72  *          ML/I2S (11pin) -- GPIO18
73  *          MC/IWL (12pin) -- GPIO17
74  *          MD/DM  (13pin) -- GPIO16
75  *          MUTE   (14pin) -- GPIO01
76  *
77  *  WM8776
78  *     A 2ch-ADC(with 10ch-selector) plus 2ch-DAC.
79  *     It's control wire was connected to SDA/SCLK (2-wire serial interface)
80  *          MODE (16pin) -- R(1K) -- GND
81  *          CE   (17pin) -- R(1K) -- GND  2-wire mode (address=0x34)
82  *          DI   (18pin) -- SDA
83  *          CL   (19pin) -- SCLK
84  *
85  *
86  * ** output pins and device names **
87  *
88  *   7.1ch name -- output connector color -- device (-D option)
89  *
90  *      FRONT 2ch                  -- green  -- plughw:0,0
91  *      CENTER(Lch) SUBWOOFER(Rch) -- black  -- plughw:0,2,0
92  *      SURROUND 2ch               -- orange -- plughw:0,2,1
93  *      SURROUND BACK 2ch          -- white  -- plughw:0,2,2
94  *
95  */
96 
97 
98 /****************************************************************************/
99 /*  WM8740 interface                                                        */
100 /****************************************************************************/
101 
102 static void se200pci_WM8740_init(struct snd_ice1712 *ice)
103 {
104 	/* nothing to do */
105 }
106 
107 
108 static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice,
109 						unsigned int rate)
110 {
111 	/* nothing to do */
112 }
113 
114 
115 /****************************************************************************/
116 /*  WM8766 interface                                                        */
117 /****************************************************************************/
118 
119 static void se200pci_WM8766_write(struct snd_ice1712 *ice,
120 					unsigned int addr, unsigned int data)
121 {
122 	unsigned int st;
123 	unsigned int bits;
124 	int i;
125 	const unsigned int DATA  = 0x010000;
126 	const unsigned int CLOCK = 0x020000;
127 	const unsigned int LOAD  = 0x040000;
128 	const unsigned int ALL_MASK = (DATA | CLOCK | LOAD);
129 
130 	snd_ice1712_save_gpio_status(ice);
131 
132 	st = ((addr & 0x7f) << 9) | (data & 0x1ff);
133 	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK);
134 	snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK);
135 	bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK;
136 
137 	snd_ice1712_gpio_write(ice, bits);
138 	for (i = 0; i < 16; i++) {
139 		udelay(1);
140 		bits &= ~CLOCK;
141 		st = (st << 1);
142 		if (st & 0x10000)
143 			bits |= DATA;
144 		else
145 			bits &= ~DATA;
146 
147 		snd_ice1712_gpio_write(ice, bits);
148 
149 		udelay(1);
150 		bits |= CLOCK;
151 		snd_ice1712_gpio_write(ice, bits);
152 	}
153 
154 	udelay(1);
155 	bits |= LOAD;
156 	snd_ice1712_gpio_write(ice, bits);
157 
158 	udelay(1);
159 	bits |= (DATA | CLOCK);
160 	snd_ice1712_gpio_write(ice, bits);
161 
162 	snd_ice1712_restore_gpio_status(ice);
163 }
164 
165 static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
166 					unsigned int vol1, unsigned int vol2)
167 {
168 	switch (ch) {
169 	case 0:
170 		se200pci_WM8766_write(ice, 0x000, vol1);
171 		se200pci_WM8766_write(ice, 0x001, vol2 | 0x100);
172 		break;
173 	case 1:
174 		se200pci_WM8766_write(ice, 0x004, vol1);
175 		se200pci_WM8766_write(ice, 0x005, vol2 | 0x100);
176 		break;
177 	case 2:
178 		se200pci_WM8766_write(ice, 0x006, vol1);
179 		se200pci_WM8766_write(ice, 0x007, vol2 | 0x100);
180 		break;
181 	}
182 }
183 
184 static void se200pci_WM8766_init(struct snd_ice1712 *ice)
185 {
186 	se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
187 	udelay(10);
188 
189 	se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */
190 	se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */
191 	se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */
192 
193 	se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */
194 	se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
195 	se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */
196 	se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */
197 	se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */
198 
199 	se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */
200 	se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */
201 }
202 
203 static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice,
204 					unsigned int rate)
205 {
206 	if (rate > 96000)
207 		se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */
208 	else
209 		se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
210 }
211 
212 
213 /****************************************************************************/
214 /*  WM8776 interface                                                        */
215 /****************************************************************************/
216 
217 static void se200pci_WM8776_write(struct snd_ice1712 *ice,
218 					unsigned int addr, unsigned int data)
219 {
220 	unsigned int val;
221 
222 	val = (addr << 9) | data;
223 	snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff);
224 }
225 
226 
227 static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice,
228 					unsigned int vol1, unsigned int vol2)
229 {
230 	se200pci_WM8776_write(ice, 0x03, vol1);
231 	se200pci_WM8776_write(ice, 0x04, vol2 | 0x100);
232 }
233 
234 static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
235 					unsigned int vol1, unsigned int vol2)
236 {
237 	se200pci_WM8776_write(ice, 0x0e, vol1);
238 	se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
239 }
240 
241 static const char * const se200pci_sel[] = {
242 	"LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
243 };
244 
245 static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
246 					       unsigned int sel)
247 {
248 	static const unsigned char vals[] = {
249 		/* LINE, CD, MIC, ALL, GND */
250 		0x10, 0x04, 0x08, 0x1c, 0x03
251 	};
252 	if (sel > 4)
253 		sel = 4;
254 	se200pci_WM8776_write(ice, 0x15, vals[sel]);
255 }
256 
257 static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
258 {
259 	/* AFL -- After Fader Listening */
260 	if (afl)
261 		se200pci_WM8776_write(ice, 0x16, 0x005);
262 	else
263 		se200pci_WM8776_write(ice, 0x16, 0x001);
264 }
265 
266 static const char * const se200pci_agc[] = {
267 	"Off", "LimiterMode", "ALCMode", NULL
268 };
269 
270 static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
271 {
272 	/* AGC -- Auto Gain Control of the input */
273 	switch (agc) {
274 	case 0:
275 		se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */
276 		break;
277 	case 1:
278 		se200pci_WM8776_write(ice, 0x10, 0x07b);
279 		se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */
280 		break;
281 	case 2:
282 		se200pci_WM8776_write(ice, 0x10, 0x1fb);
283 		se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */
284 		break;
285 	}
286 }
287 
288 static void se200pci_WM8776_init(struct snd_ice1712 *ice)
289 {
290 	int i;
291 	static const unsigned short default_values[] = {
292 		0x100, 0x100, 0x100,
293 		0x100, 0x100, 0x100,
294 		0x000, 0x090, 0x000, 0x000,
295 		0x022, 0x022, 0x022,
296 		0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
297 		0x032, 0x000, 0x0a6, 0x001, 0x001
298 	};
299 
300 	se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */
301 	/* ADC and DAC interface is I2S 24bits mode */
302  	/* The sample-rate are automatically changed */
303 	udelay(10);
304 	/* BUT my board can not do reset all, so I load all by manually. */
305 	for (i = 0; i < ARRAY_SIZE(default_values); i++)
306 		se200pci_WM8776_write(ice, i, default_values[i]);
307 
308 	se200pci_WM8776_set_input_selector(ice, 0);
309 	se200pci_WM8776_set_afl(ice, 0);
310 	se200pci_WM8776_set_agc(ice, 0);
311 	se200pci_WM8776_set_input_volume(ice, 0, 0);
312 	se200pci_WM8776_set_output_volume(ice, 0, 0);
313 
314 	/* head phone mute and power down */
315 	se200pci_WM8776_write(ice, 0x00, 0);
316 	se200pci_WM8776_write(ice, 0x01, 0);
317 	se200pci_WM8776_write(ice, 0x02, 0x100);
318 	se200pci_WM8776_write(ice, 0x0d, 0x080);
319 }
320 
321 static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice,
322 						unsigned int rate)
323 {
324 	/* nothing to do */
325 }
326 
327 
328 /****************************************************************************/
329 /*  runtime interface                                                       */
330 /****************************************************************************/
331 
332 static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
333 {
334 	se200pci_WM8740_set_pro_rate(ice, rate);
335 	se200pci_WM8766_set_pro_rate(ice, rate);
336 	se200pci_WM8776_set_pro_rate(ice, rate);
337 }
338 
339 struct se200pci_control {
340 	const char *name;
341 	enum {
342 		WM8766,
343 		WM8776in,
344 		WM8776out,
345 		WM8776sel,
346 		WM8776agc,
347 		WM8776afl
348 	} target;
349 	enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
350 	int ch;
351 	const char * const *member;
352 	const char *comment;
353 };
354 
355 static const struct se200pci_control se200pci_cont[] = {
356 	{
357 		.name = "Front Playback Volume",
358 		.target = WM8776out,
359 		.type = VOLUME1,
360 		.comment = "Front(green)"
361 	},
362 	{
363 		.name = "Side Playback Volume",
364 		.target = WM8766,
365 		.type = VOLUME1,
366 		.ch = 1,
367 		.comment = "Surround(orange)"
368 	},
369 	{
370 		.name = "Surround Playback Volume",
371 		.target = WM8766,
372 		.type = VOLUME1,
373 		.ch = 2,
374 		.comment = "SurroundBack(white)"
375 	},
376 	{
377 		.name = "CLFE Playback Volume",
378 		.target = WM8766,
379 		.type = VOLUME1,
380 		.ch = 0,
381 		.comment = "Center(Lch)&SubWoofer(Rch)(black)"
382 	},
383 	{
384 		.name = "Capture Volume",
385 		.target = WM8776in,
386 		.type = VOLUME2
387 	},
388 	{
389 		.name = "Capture Select",
390 		.target = WM8776sel,
391 		.type = ENUM,
392 		.member = se200pci_sel
393 	},
394 	{
395 		.name = "AGC Capture Mode",
396 		.target = WM8776agc,
397 		.type = ENUM,
398 		.member = se200pci_agc
399 	},
400 	{
401 		.name = "AFL Bypass Playback Switch",
402 		.target = WM8776afl,
403 		.type = BOOLEAN
404 	}
405 };
406 
407 static int se200pci_get_enum_count(int n)
408 {
409 	const char * const *member;
410 	int c;
411 
412 	member = se200pci_cont[n].member;
413 	if (!member)
414 		return 0;
415 	for (c = 0; member[c]; c++)
416 		;
417 	return c;
418 }
419 
420 static int se200pci_cont_volume_info(struct snd_kcontrol *kc,
421 				     struct snd_ctl_elem_info *uinfo)
422 {
423 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
424 	uinfo->count = 2;
425 	uinfo->value.integer.min = 0; /* mute */
426 	uinfo->value.integer.max = 0xff; /* 0dB */
427 	return 0;
428 }
429 
430 #define se200pci_cont_boolean_info	snd_ctl_boolean_mono_info
431 
432 static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
433 				   struct snd_ctl_elem_info *uinfo)
434 {
435 	int n, c;
436 
437 	n = kc->private_value;
438 	c = se200pci_get_enum_count(n);
439 	if (!c)
440 		return -EINVAL;
441 	return snd_ctl_enum_info(uinfo, 1, c, se200pci_cont[n].member);
442 }
443 
444 static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
445 				    struct snd_ctl_elem_value *uc)
446 {
447 	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
448 	struct se_spec *spec = ice->spec;
449 	int n = kc->private_value;
450 	uc->value.integer.value[0] = spec->vol[n].ch1;
451 	uc->value.integer.value[1] = spec->vol[n].ch2;
452 	return 0;
453 }
454 
455 static int se200pci_cont_boolean_get(struct snd_kcontrol *kc,
456 				     struct snd_ctl_elem_value *uc)
457 {
458 	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
459 	struct se_spec *spec = ice->spec;
460 	int n = kc->private_value;
461 	uc->value.integer.value[0] = spec->vol[n].ch1;
462 	return 0;
463 }
464 
465 static int se200pci_cont_enum_get(struct snd_kcontrol *kc,
466 				  struct snd_ctl_elem_value *uc)
467 {
468 	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
469 	struct se_spec *spec = ice->spec;
470 	int n = kc->private_value;
471 	uc->value.enumerated.item[0] = spec->vol[n].ch1;
472 	return 0;
473 }
474 
475 static void se200pci_cont_update(struct snd_ice1712 *ice, int n)
476 {
477 	struct se_spec *spec = ice->spec;
478 	switch (se200pci_cont[n].target) {
479 	case WM8766:
480 		se200pci_WM8766_set_volume(ice,
481 					   se200pci_cont[n].ch,
482 					   spec->vol[n].ch1,
483 					   spec->vol[n].ch2);
484 		break;
485 
486 	case WM8776in:
487 		se200pci_WM8776_set_input_volume(ice,
488 						 spec->vol[n].ch1,
489 						 spec->vol[n].ch2);
490 		break;
491 
492 	case WM8776out:
493 		se200pci_WM8776_set_output_volume(ice,
494 						  spec->vol[n].ch1,
495 						  spec->vol[n].ch2);
496 		break;
497 
498 	case WM8776sel:
499 		se200pci_WM8776_set_input_selector(ice,
500 						   spec->vol[n].ch1);
501 		break;
502 
503 	case WM8776agc:
504 		se200pci_WM8776_set_agc(ice, spec->vol[n].ch1);
505 		break;
506 
507 	case WM8776afl:
508 		se200pci_WM8776_set_afl(ice, spec->vol[n].ch1);
509 		break;
510 
511 	default:
512 		break;
513 	}
514 }
515 
516 static int se200pci_cont_volume_put(struct snd_kcontrol *kc,
517 				    struct snd_ctl_elem_value *uc)
518 {
519 	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
520 	struct se_spec *spec = ice->spec;
521 	int n = kc->private_value;
522 	unsigned int vol1, vol2;
523 	int changed;
524 
525 	changed = 0;
526 	vol1 = uc->value.integer.value[0] & 0xff;
527 	vol2 = uc->value.integer.value[1] & 0xff;
528 	if (spec->vol[n].ch1 != vol1) {
529 		spec->vol[n].ch1 = vol1;
530 		changed = 1;
531 	}
532 	if (spec->vol[n].ch2 != vol2) {
533 		spec->vol[n].ch2 = vol2;
534 		changed = 1;
535 	}
536 	if (changed)
537 		se200pci_cont_update(ice, n);
538 
539 	return changed;
540 }
541 
542 static int se200pci_cont_boolean_put(struct snd_kcontrol *kc,
543 				     struct snd_ctl_elem_value *uc)
544 {
545 	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
546 	struct se_spec *spec = ice->spec;
547 	int n = kc->private_value;
548 	unsigned int vol1;
549 
550 	vol1 = !!uc->value.integer.value[0];
551 	if (spec->vol[n].ch1 != vol1) {
552 		spec->vol[n].ch1 = vol1;
553 		se200pci_cont_update(ice, n);
554 		return 1;
555 	}
556 	return 0;
557 }
558 
559 static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
560 				  struct snd_ctl_elem_value *uc)
561 {
562 	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
563 	struct se_spec *spec = ice->spec;
564 	int n = kc->private_value;
565 	unsigned int vol1;
566 
567 	vol1 = uc->value.enumerated.item[0];
568 	if (vol1 >= se200pci_get_enum_count(n))
569 		return -EINVAL;
570 	if (spec->vol[n].ch1 != vol1) {
571 		spec->vol[n].ch1 = vol1;
572 		se200pci_cont_update(ice, n);
573 		return 1;
574 	}
575 	return 0;
576 }
577 
578 static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
579 static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
580 
581 static int se200pci_add_controls(struct snd_ice1712 *ice)
582 {
583 	int i;
584 	struct snd_kcontrol_new cont;
585 	int err;
586 
587 	memset(&cont, 0, sizeof(cont));
588 	cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
589 	for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) {
590 		cont.private_value = i;
591 		cont.name = se200pci_cont[i].name;
592 		cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
593 		cont.tlv.p = NULL;
594 		switch (se200pci_cont[i].type) {
595 		case VOLUME1:
596 		case VOLUME2:
597 			cont.info = se200pci_cont_volume_info;
598 			cont.get = se200pci_cont_volume_get;
599 			cont.put = se200pci_cont_volume_put;
600 			cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
601 			if (se200pci_cont[i].type == VOLUME1)
602 				cont.tlv.p = db_scale_gain1;
603 			else
604 				cont.tlv.p = db_scale_gain2;
605 			break;
606 		case BOOLEAN:
607 			cont.info = se200pci_cont_boolean_info;
608 			cont.get = se200pci_cont_boolean_get;
609 			cont.put = se200pci_cont_boolean_put;
610 			break;
611 		case ENUM:
612 			cont.info = se200pci_cont_enum_info;
613 			cont.get = se200pci_cont_enum_get;
614 			cont.put = se200pci_cont_enum_put;
615 			break;
616 		default:
617 			snd_BUG();
618 			return -EINVAL;
619 		}
620 		err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice));
621 		if (err < 0)
622 			return err;
623 	}
624 
625 	return 0;
626 }
627 
628 
629 /****************************************************************************/
630 /*  ONKYO WAVIO SE-90PCI                                                    */
631 /****************************************************************************/
632 /*
633  *  system configuration ICE_EEP2_SYSCONF=0x4b
634  *  AC-Link configuration ICE_EEP2_ACLINK=0x80
635  *  I2S converters feature ICE_EEP2_I2S=0x78
636  *  S/PDIF configuration ICE_EEP2_SPDIF=0xc3
637  *
638  *  ** connected chip **
639  *
640  *   WM8716
641  *      A 2ch-DAC of main outputs.
642  *      It setuped as I2S mode by wire, so no way to setup from software.
643  *         ML/I2S (28pin) -- +5V
644  *         MC/DM1 (27pin) -- GND
645  *         MC/DM0 (26pin) -- GND
646  *         MUTEB  (25pin) -- open (internal pull-up)
647  *         MODE   (24pin) -- GND
648  *         CSBIWO (23pin) -- +5V
649  *
650  */
651 
652  /* Nothing to do for this chip. */
653 
654 
655 /****************************************************************************/
656 /*  probe/initialize/setup                                                  */
657 /****************************************************************************/
658 
659 static int se_init(struct snd_ice1712 *ice)
660 {
661 	struct se_spec *spec;
662 
663 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
664 	if (!spec)
665 		return -ENOMEM;
666 	ice->spec = spec;
667 
668 	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) {
669 		ice->num_total_dacs = 2;
670 		ice->num_total_adcs = 0;
671 		ice->vt1720 = 1;
672 		return 0;
673 
674 	} else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) {
675 		ice->num_total_dacs = 8;
676 		ice->num_total_adcs = 2;
677 		se200pci_WM8740_init(ice);
678 		se200pci_WM8766_init(ice);
679 		se200pci_WM8776_init(ice);
680 		ice->gpio.set_pro_rate = se200pci_set_pro_rate;
681 		return 0;
682 	}
683 
684 	return -ENOENT;
685 }
686 
687 static int se_add_controls(struct snd_ice1712 *ice)
688 {
689 	int err;
690 
691 	err = 0;
692 	/* nothing to do for VT1724_SUBDEVICE_SE90PCI */
693 	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI)
694 		err = se200pci_add_controls(ice);
695 
696 	return err;
697 }
698 
699 
700 /****************************************************************************/
701 /*  entry point                                                             */
702 /****************************************************************************/
703 
704 static const unsigned char se200pci_eeprom[] = {
705 	[ICE_EEP2_SYSCONF]	= 0x4b,	/* 49.152Hz, spdif-in/ADC, 4DACs */
706 	[ICE_EEP2_ACLINK]	= 0x80,	/* I2S */
707 	[ICE_EEP2_I2S]		= 0x78,	/* 96k-ok, 24bit, 192k-ok */
708 	[ICE_EEP2_SPDIF]	= 0xc3,	/* out-en, out-int, spdif-in */
709 
710 	[ICE_EEP2_GPIO_DIR]	= 0x02, /* WM8766 mute      1=output */
711 	[ICE_EEP2_GPIO_DIR1]	= 0x00, /* not used */
712 	[ICE_EEP2_GPIO_DIR2]	= 0x07, /* WM8766 ML/MC/MD  1=output */
713 
714 	[ICE_EEP2_GPIO_MASK]	= 0x00, /* 0=writable */
715 	[ICE_EEP2_GPIO_MASK1]	= 0x00, /* 0=writable */
716 	[ICE_EEP2_GPIO_MASK2]	= 0x00, /* 0=writable */
717 
718 	[ICE_EEP2_GPIO_STATE]	= 0x00, /* WM8766 mute=0 */
719 	[ICE_EEP2_GPIO_STATE1]	= 0x00, /* not used */
720 	[ICE_EEP2_GPIO_STATE2]	= 0x07, /* WM8766 ML/MC/MD */
721 };
722 
723 static const unsigned char se90pci_eeprom[] = {
724 	[ICE_EEP2_SYSCONF]	= 0x4b,	/* 49.152Hz, spdif-in/ADC, 4DACs */
725 	[ICE_EEP2_ACLINK]	= 0x80,	/* I2S */
726 	[ICE_EEP2_I2S]		= 0x78,	/* 96k-ok, 24bit, 192k-ok */
727 	[ICE_EEP2_SPDIF]	= 0xc3,	/* out-en, out-int, spdif-in */
728 
729 	/* ALL GPIO bits are in input mode */
730 };
731 
732 struct snd_ice1712_card_info snd_vt1724_se_cards[] = {
733 	{
734 		.subvendor = VT1724_SUBDEVICE_SE200PCI,
735 		.name = "ONKYO SE200PCI",
736 		.model = "se200pci",
737 		.chip_init = se_init,
738 		.build_controls = se_add_controls,
739 		.eeprom_size = sizeof(se200pci_eeprom),
740 		.eeprom_data = se200pci_eeprom,
741 	},
742 	{
743 		.subvendor = VT1724_SUBDEVICE_SE90PCI,
744 		.name = "ONKYO SE90PCI",
745 		.model = "se90pci",
746 		.chip_init = se_init,
747 		.build_controls = se_add_controls,
748 		.eeprom_size = sizeof(se90pci_eeprom),
749 		.eeprom_data = se90pci_eeprom,
750 	},
751 	{} /*terminator*/
752 };
753