cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

se.c (19875B)


      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
     22struct 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
    102static void se200pci_WM8740_init(struct snd_ice1712 *ice)
    103{
    104	/* nothing to do */
    105}
    106
    107
    108static 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
    119static 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
    165static 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
    184static 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
    203static 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
    217static 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
    227static 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
    234static 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
    241static const char * const se200pci_sel[] = {
    242	"LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
    243};
    244
    245static 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
    257static 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
    266static const char * const se200pci_agc[] = {
    267	"Off", "LimiterMode", "ALCMode", NULL
    268};
    269
    270static 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
    288static 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
    321static 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
    332static 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
    339struct 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
    355static 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
    407static 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
    420static 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
    432static 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
    444static 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
    455static 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
    465static 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
    475static 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
    516static 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
    542static 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
    559static 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
    578static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
    579static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
    580
    581static 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
    659static 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
    687static 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
    704static 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
    723static 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
    732struct 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};