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