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