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