1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * ALSA driver for ICEnsemble VT17xx 4 * 5 * Lowlevel functions for WM8776 codec 6 * 7 * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> 8 */ 9 10 #include <linux/delay.h> 11 #include <sound/core.h> 12 #include <sound/control.h> 13 #include <sound/tlv.h> 14 #include "wm8776.h" 15 16 /* low-level access */ 17 18 static void snd_wm8776_write(struct snd_wm8776 *wm, u16 addr, u16 data) 19 { 20 u8 bus_addr = addr << 1 | data >> 8; /* addr + 9th data bit */ 21 u8 bus_data = data & 0xff; /* remaining 8 data bits */ 22 23 if (addr < WM8776_REG_RESET) 24 wm->regs[addr] = data; 25 wm->ops.write(wm, bus_addr, bus_data); 26 } 27 28 /* register-level functions */ 29 30 static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm, 31 const char *ctl_name, 32 bool active) 33 { 34 struct snd_card *card = wm->card; 35 struct snd_kcontrol *kctl; 36 struct snd_kcontrol_volatile *vd; 37 struct snd_ctl_elem_id elem_id; 38 unsigned int index_offset; 39 40 memset(&elem_id, 0, sizeof(elem_id)); 41 strscpy(elem_id.name, ctl_name, sizeof(elem_id.name)); 42 elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 43 kctl = snd_ctl_find_id(card, &elem_id); 44 if (!kctl) 45 return; 46 index_offset = snd_ctl_get_ioff(kctl, &kctl->id); 47 vd = &kctl->vd[index_offset]; 48 if (active) 49 vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 50 else 51 vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 52 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); 53 } 54 55 static void snd_wm8776_update_agc_ctl(struct snd_wm8776 *wm) 56 { 57 int i, flags_on = 0, flags_off = 0; 58 59 switch (wm->agc_mode) { 60 case WM8776_AGC_OFF: 61 flags_off = WM8776_FLAG_LIM | WM8776_FLAG_ALC; 62 break; 63 case WM8776_AGC_LIM: 64 flags_off = WM8776_FLAG_ALC; 65 flags_on = WM8776_FLAG_LIM; 66 break; 67 case WM8776_AGC_ALC_R: 68 case WM8776_AGC_ALC_L: 69 case WM8776_AGC_ALC_STEREO: 70 flags_off = WM8776_FLAG_LIM; 71 flags_on = WM8776_FLAG_ALC; 72 break; 73 } 74 75 for (i = 0; i < WM8776_CTL_COUNT; i++) 76 if (wm->ctl[i].flags & flags_off) 77 snd_wm8776_activate_ctl(wm, wm->ctl[i].name, false); 78 else if (wm->ctl[i].flags & flags_on) 79 snd_wm8776_activate_ctl(wm, wm->ctl[i].name, true); 80 } 81 82 static void snd_wm8776_set_agc(struct snd_wm8776 *wm, u16 agc, u16 nothing) 83 { 84 u16 alc1 = wm->regs[WM8776_REG_ALCCTRL1] & ~WM8776_ALC1_LCT_MASK; 85 u16 alc2 = wm->regs[WM8776_REG_ALCCTRL2] & ~WM8776_ALC2_LCEN; 86 87 switch (agc) { 88 case 0: /* Off */ 89 wm->agc_mode = WM8776_AGC_OFF; 90 break; 91 case 1: /* Limiter */ 92 alc2 |= WM8776_ALC2_LCEN; 93 wm->agc_mode = WM8776_AGC_LIM; 94 break; 95 case 2: /* ALC Right */ 96 alc1 |= WM8776_ALC1_LCSEL_ALCR; 97 alc2 |= WM8776_ALC2_LCEN; 98 wm->agc_mode = WM8776_AGC_ALC_R; 99 break; 100 case 3: /* ALC Left */ 101 alc1 |= WM8776_ALC1_LCSEL_ALCL; 102 alc2 |= WM8776_ALC2_LCEN; 103 wm->agc_mode = WM8776_AGC_ALC_L; 104 break; 105 case 4: /* ALC Stereo */ 106 alc1 |= WM8776_ALC1_LCSEL_ALCSTEREO; 107 alc2 |= WM8776_ALC2_LCEN; 108 wm->agc_mode = WM8776_AGC_ALC_STEREO; 109 break; 110 } 111 snd_wm8776_write(wm, WM8776_REG_ALCCTRL1, alc1); 112 snd_wm8776_write(wm, WM8776_REG_ALCCTRL2, alc2); 113 snd_wm8776_update_agc_ctl(wm); 114 } 115 116 static void snd_wm8776_get_agc(struct snd_wm8776 *wm, u16 *mode, u16 *nothing) 117 { 118 *mode = wm->agc_mode; 119 } 120 121 /* mixer controls */ 122 123 static const DECLARE_TLV_DB_SCALE(wm8776_hp_tlv, -7400, 100, 1); 124 static const DECLARE_TLV_DB_SCALE(wm8776_dac_tlv, -12750, 50, 1); 125 static const DECLARE_TLV_DB_SCALE(wm8776_adc_tlv, -10350, 50, 1); 126 static const DECLARE_TLV_DB_SCALE(wm8776_lct_tlv, -1600, 100, 0); 127 static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_tlv, 0, 400, 0); 128 static const DECLARE_TLV_DB_SCALE(wm8776_ngth_tlv, -7800, 600, 0); 129 static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_tlv, -1200, 100, 0); 130 static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_tlv, -2100, 400, 0); 131 132 static const struct snd_wm8776_ctl snd_wm8776_default_ctl[WM8776_CTL_COUNT] = { 133 [WM8776_CTL_DAC_VOL] = { 134 .name = "Master Playback Volume", 135 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 136 .tlv = wm8776_dac_tlv, 137 .reg1 = WM8776_REG_DACLVOL, 138 .reg2 = WM8776_REG_DACRVOL, 139 .mask1 = WM8776_DACVOL_MASK, 140 .mask2 = WM8776_DACVOL_MASK, 141 .max = 0xff, 142 .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE, 143 }, 144 [WM8776_CTL_DAC_SW] = { 145 .name = "Master Playback Switch", 146 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 147 .reg1 = WM8776_REG_DACCTRL1, 148 .reg2 = WM8776_REG_DACCTRL1, 149 .mask1 = WM8776_DAC_PL_LL, 150 .mask2 = WM8776_DAC_PL_RR, 151 .flags = WM8776_FLAG_STEREO, 152 }, 153 [WM8776_CTL_DAC_ZC_SW] = { 154 .name = "Master Zero Cross Detect Playback Switch", 155 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 156 .reg1 = WM8776_REG_DACCTRL1, 157 .mask1 = WM8776_DAC_DZCEN, 158 }, 159 [WM8776_CTL_HP_VOL] = { 160 .name = "Headphone Playback Volume", 161 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 162 .tlv = wm8776_hp_tlv, 163 .reg1 = WM8776_REG_HPLVOL, 164 .reg2 = WM8776_REG_HPRVOL, 165 .mask1 = WM8776_HPVOL_MASK, 166 .mask2 = WM8776_HPVOL_MASK, 167 .min = 0x2f, 168 .max = 0x7f, 169 .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE, 170 }, 171 [WM8776_CTL_HP_SW] = { 172 .name = "Headphone Playback Switch", 173 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 174 .reg1 = WM8776_REG_PWRDOWN, 175 .mask1 = WM8776_PWR_HPPD, 176 .flags = WM8776_FLAG_INVERT, 177 }, 178 [WM8776_CTL_HP_ZC_SW] = { 179 .name = "Headphone Zero Cross Detect Playback Switch", 180 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 181 .reg1 = WM8776_REG_HPLVOL, 182 .reg2 = WM8776_REG_HPRVOL, 183 .mask1 = WM8776_VOL_HPZCEN, 184 .mask2 = WM8776_VOL_HPZCEN, 185 .flags = WM8776_FLAG_STEREO, 186 }, 187 [WM8776_CTL_AUX_SW] = { 188 .name = "AUX Playback Switch", 189 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 190 .reg1 = WM8776_REG_OUTMUX, 191 .mask1 = WM8776_OUTMUX_AUX, 192 }, 193 [WM8776_CTL_BYPASS_SW] = { 194 .name = "Bypass Playback Switch", 195 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 196 .reg1 = WM8776_REG_OUTMUX, 197 .mask1 = WM8776_OUTMUX_BYPASS, 198 }, 199 [WM8776_CTL_DAC_IZD_SW] = { 200 .name = "Infinite Zero Detect Playback Switch", 201 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 202 .reg1 = WM8776_REG_DACCTRL1, 203 .mask1 = WM8776_DAC_IZD, 204 }, 205 [WM8776_CTL_PHASE_SW] = { 206 .name = "Phase Invert Playback Switch", 207 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 208 .reg1 = WM8776_REG_PHASESWAP, 209 .reg2 = WM8776_REG_PHASESWAP, 210 .mask1 = WM8776_PHASE_INVERTL, 211 .mask2 = WM8776_PHASE_INVERTR, 212 .flags = WM8776_FLAG_STEREO, 213 }, 214 [WM8776_CTL_DEEMPH_SW] = { 215 .name = "Deemphasis Playback Switch", 216 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 217 .reg1 = WM8776_REG_DACCTRL2, 218 .mask1 = WM8776_DAC2_DEEMPH, 219 }, 220 [WM8776_CTL_ADC_VOL] = { 221 .name = "Input Capture Volume", 222 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 223 .tlv = wm8776_adc_tlv, 224 .reg1 = WM8776_REG_ADCLVOL, 225 .reg2 = WM8776_REG_ADCRVOL, 226 .mask1 = WM8776_ADC_GAIN_MASK, 227 .mask2 = WM8776_ADC_GAIN_MASK, 228 .max = 0xff, 229 .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE, 230 }, 231 [WM8776_CTL_ADC_SW] = { 232 .name = "Input Capture Switch", 233 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 234 .reg1 = WM8776_REG_ADCMUX, 235 .reg2 = WM8776_REG_ADCMUX, 236 .mask1 = WM8776_ADC_MUTEL, 237 .mask2 = WM8776_ADC_MUTER, 238 .flags = WM8776_FLAG_STEREO | WM8776_FLAG_INVERT, 239 }, 240 [WM8776_CTL_INPUT1_SW] = { 241 .name = "AIN1 Capture Switch", 242 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 243 .reg1 = WM8776_REG_ADCMUX, 244 .mask1 = WM8776_ADC_MUX_AIN1, 245 }, 246 [WM8776_CTL_INPUT2_SW] = { 247 .name = "AIN2 Capture Switch", 248 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 249 .reg1 = WM8776_REG_ADCMUX, 250 .mask1 = WM8776_ADC_MUX_AIN2, 251 }, 252 [WM8776_CTL_INPUT3_SW] = { 253 .name = "AIN3 Capture Switch", 254 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 255 .reg1 = WM8776_REG_ADCMUX, 256 .mask1 = WM8776_ADC_MUX_AIN3, 257 }, 258 [WM8776_CTL_INPUT4_SW] = { 259 .name = "AIN4 Capture Switch", 260 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 261 .reg1 = WM8776_REG_ADCMUX, 262 .mask1 = WM8776_ADC_MUX_AIN4, 263 }, 264 [WM8776_CTL_INPUT5_SW] = { 265 .name = "AIN5 Capture Switch", 266 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 267 .reg1 = WM8776_REG_ADCMUX, 268 .mask1 = WM8776_ADC_MUX_AIN5, 269 }, 270 [WM8776_CTL_AGC_SEL] = { 271 .name = "AGC Select Capture Enum", 272 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 273 .enum_names = { "Off", "Limiter", "ALC Right", "ALC Left", 274 "ALC Stereo" }, 275 .max = 5, /* .enum_names item count */ 276 .set = snd_wm8776_set_agc, 277 .get = snd_wm8776_get_agc, 278 }, 279 [WM8776_CTL_LIM_THR] = { 280 .name = "Limiter Threshold Capture Volume", 281 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 282 .tlv = wm8776_lct_tlv, 283 .reg1 = WM8776_REG_ALCCTRL1, 284 .mask1 = WM8776_ALC1_LCT_MASK, 285 .max = 15, 286 .flags = WM8776_FLAG_LIM, 287 }, 288 [WM8776_CTL_LIM_ATK] = { 289 .name = "Limiter Attack Time Capture Enum", 290 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 291 .enum_names = { "0.25 ms", "0.5 ms", "1 ms", "2 ms", "4 ms", 292 "8 ms", "16 ms", "32 ms", "64 ms", "128 ms", "256 ms" }, 293 .max = 11, /* .enum_names item count */ 294 .reg1 = WM8776_REG_ALCCTRL3, 295 .mask1 = WM8776_ALC3_ATK_MASK, 296 .flags = WM8776_FLAG_LIM, 297 }, 298 [WM8776_CTL_LIM_DCY] = { 299 .name = "Limiter Decay Time Capture Enum", 300 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 301 .enum_names = { "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms", 302 "19.2 ms", "38.4 ms", "76.8 ms", "154 ms", "307 ms", 303 "614 ms", "1.23 s" }, 304 .max = 11, /* .enum_names item count */ 305 .reg1 = WM8776_REG_ALCCTRL3, 306 .mask1 = WM8776_ALC3_DCY_MASK, 307 .flags = WM8776_FLAG_LIM, 308 }, 309 [WM8776_CTL_LIM_TRANWIN] = { 310 .name = "Limiter Transient Window Capture Enum", 311 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 312 .enum_names = { "0 us", "62.5 us", "125 us", "250 us", "500 us", 313 "1 ms", "2 ms", "4 ms" }, 314 .max = 8, /* .enum_names item count */ 315 .reg1 = WM8776_REG_LIMITER, 316 .mask1 = WM8776_LIM_TRANWIN_MASK, 317 .flags = WM8776_FLAG_LIM, 318 }, 319 [WM8776_CTL_LIM_MAXATTN] = { 320 .name = "Limiter Maximum Attenuation Capture Volume", 321 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 322 .tlv = wm8776_maxatten_lim_tlv, 323 .reg1 = WM8776_REG_LIMITER, 324 .mask1 = WM8776_LIM_MAXATTEN_MASK, 325 .min = 3, 326 .max = 12, 327 .flags = WM8776_FLAG_LIM | WM8776_FLAG_INVERT, 328 }, 329 [WM8776_CTL_ALC_TGT] = { 330 .name = "ALC Target Level Capture Volume", 331 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 332 .tlv = wm8776_lct_tlv, 333 .reg1 = WM8776_REG_ALCCTRL1, 334 .mask1 = WM8776_ALC1_LCT_MASK, 335 .max = 15, 336 .flags = WM8776_FLAG_ALC, 337 }, 338 [WM8776_CTL_ALC_ATK] = { 339 .name = "ALC Attack Time Capture Enum", 340 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 341 .enum_names = { "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms", 342 "134 ms", "269 ms", "538 ms", "1.08 s", "2.15 s", 343 "4.3 s", "8.6 s" }, 344 .max = 11, /* .enum_names item count */ 345 .reg1 = WM8776_REG_ALCCTRL3, 346 .mask1 = WM8776_ALC3_ATK_MASK, 347 .flags = WM8776_FLAG_ALC, 348 }, 349 [WM8776_CTL_ALC_DCY] = { 350 .name = "ALC Decay Time Capture Enum", 351 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 352 .enum_names = { "33.5 ms", "67.0 ms", "134 ms", "268 ms", 353 "536 ms", "1.07 s", "2.14 s", "4.29 s", "8.58 s", 354 "17.2 s", "34.3 s" }, 355 .max = 11, /* .enum_names item count */ 356 .reg1 = WM8776_REG_ALCCTRL3, 357 .mask1 = WM8776_ALC3_DCY_MASK, 358 .flags = WM8776_FLAG_ALC, 359 }, 360 [WM8776_CTL_ALC_MAXGAIN] = { 361 .name = "ALC Maximum Gain Capture Volume", 362 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 363 .tlv = wm8776_maxgain_tlv, 364 .reg1 = WM8776_REG_ALCCTRL1, 365 .mask1 = WM8776_ALC1_MAXGAIN_MASK, 366 .min = 1, 367 .max = 7, 368 .flags = WM8776_FLAG_ALC, 369 }, 370 [WM8776_CTL_ALC_MAXATTN] = { 371 .name = "ALC Maximum Attenuation Capture Volume", 372 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 373 .tlv = wm8776_maxatten_alc_tlv, 374 .reg1 = WM8776_REG_LIMITER, 375 .mask1 = WM8776_LIM_MAXATTEN_MASK, 376 .min = 10, 377 .max = 15, 378 .flags = WM8776_FLAG_ALC | WM8776_FLAG_INVERT, 379 }, 380 [WM8776_CTL_ALC_HLD] = { 381 .name = "ALC Hold Time Capture Enum", 382 .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, 383 .enum_names = { "0 ms", "2.67 ms", "5.33 ms", "10.6 ms", 384 "21.3 ms", "42.7 ms", "85.3 ms", "171 ms", "341 ms", 385 "683 ms", "1.37 s", "2.73 s", "5.46 s", "10.9 s", 386 "21.8 s", "43.7 s" }, 387 .max = 16, /* .enum_names item count */ 388 .reg1 = WM8776_REG_ALCCTRL2, 389 .mask1 = WM8776_ALC2_HOLD_MASK, 390 .flags = WM8776_FLAG_ALC, 391 }, 392 [WM8776_CTL_NGT_SW] = { 393 .name = "Noise Gate Capture Switch", 394 .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 395 .reg1 = WM8776_REG_NOISEGATE, 396 .mask1 = WM8776_NGAT_ENABLE, 397 .flags = WM8776_FLAG_ALC, 398 }, 399 [WM8776_CTL_NGT_THR] = { 400 .name = "Noise Gate Threshold Capture Volume", 401 .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 402 .tlv = wm8776_ngth_tlv, 403 .reg1 = WM8776_REG_NOISEGATE, 404 .mask1 = WM8776_NGAT_THR_MASK, 405 .max = 7, 406 .flags = WM8776_FLAG_ALC, 407 }, 408 }; 409 410 /* exported functions */ 411 412 void snd_wm8776_init(struct snd_wm8776 *wm) 413 { 414 int i; 415 static const u16 default_values[] = { 416 0x000, 0x100, 0x000, 417 0x000, 0x100, 0x000, 418 0x000, 0x090, 0x000, 0x000, 419 0x022, 0x022, 0x022, 420 0x008, 0x0cf, 0x0cf, 0x07b, 0x000, 421 0x032, 0x000, 0x0a6, 0x001, 0x001 422 }; 423 424 memcpy(wm->ctl, snd_wm8776_default_ctl, sizeof(wm->ctl)); 425 426 snd_wm8776_write(wm, WM8776_REG_RESET, 0x00); /* reset */ 427 udelay(10); 428 /* load defaults */ 429 for (i = 0; i < ARRAY_SIZE(default_values); i++) 430 snd_wm8776_write(wm, i, default_values[i]); 431 } 432 433 void snd_wm8776_resume(struct snd_wm8776 *wm) 434 { 435 int i; 436 437 for (i = 0; i < WM8776_REG_COUNT; i++) 438 snd_wm8776_write(wm, i, wm->regs[i]); 439 } 440 441 void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power) 442 { 443 snd_wm8776_write(wm, WM8776_REG_PWRDOWN, power); 444 } 445 446 void snd_wm8776_volume_restore(struct snd_wm8776 *wm) 447 { 448 u16 val = wm->regs[WM8776_REG_DACRVOL]; 449 /* restore volume after MCLK stopped */ 450 snd_wm8776_write(wm, WM8776_REG_DACRVOL, val | WM8776_VOL_UPDATE); 451 } 452 453 /* mixer callbacks */ 454 455 static int snd_wm8776_volume_info(struct snd_kcontrol *kcontrol, 456 struct snd_ctl_elem_info *uinfo) 457 { 458 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 459 int n = kcontrol->private_value; 460 461 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 462 uinfo->count = (wm->ctl[n].flags & WM8776_FLAG_STEREO) ? 2 : 1; 463 uinfo->value.integer.min = wm->ctl[n].min; 464 uinfo->value.integer.max = wm->ctl[n].max; 465 466 return 0; 467 } 468 469 static int snd_wm8776_enum_info(struct snd_kcontrol *kcontrol, 470 struct snd_ctl_elem_info *uinfo) 471 { 472 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 473 int n = kcontrol->private_value; 474 475 return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max, 476 wm->ctl[n].enum_names); 477 } 478 479 static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol, 480 struct snd_ctl_elem_value *ucontrol) 481 { 482 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 483 int n = kcontrol->private_value; 484 u16 val1, val2; 485 486 if (wm->ctl[n].get) 487 wm->ctl[n].get(wm, &val1, &val2); 488 else { 489 val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1; 490 val1 >>= __ffs(wm->ctl[n].mask1); 491 if (wm->ctl[n].flags & WM8776_FLAG_STEREO) { 492 val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2; 493 val2 >>= __ffs(wm->ctl[n].mask2); 494 if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE) 495 val2 &= ~WM8776_VOL_UPDATE; 496 } 497 } 498 if (wm->ctl[n].flags & WM8776_FLAG_INVERT) { 499 val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); 500 if (wm->ctl[n].flags & WM8776_FLAG_STEREO) 501 val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); 502 } 503 ucontrol->value.integer.value[0] = val1; 504 if (wm->ctl[n].flags & WM8776_FLAG_STEREO) 505 ucontrol->value.integer.value[1] = val2; 506 507 return 0; 508 } 509 510 static int snd_wm8776_ctl_put(struct snd_kcontrol *kcontrol, 511 struct snd_ctl_elem_value *ucontrol) 512 { 513 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 514 int n = kcontrol->private_value; 515 u16 val, regval1, regval2; 516 517 /* this also works for enum because value is a union */ 518 regval1 = ucontrol->value.integer.value[0]; 519 regval2 = ucontrol->value.integer.value[1]; 520 if (wm->ctl[n].flags & WM8776_FLAG_INVERT) { 521 regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min); 522 regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min); 523 } 524 if (wm->ctl[n].set) 525 wm->ctl[n].set(wm, regval1, regval2); 526 else { 527 val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1; 528 val |= regval1 << __ffs(wm->ctl[n].mask1); 529 /* both stereo controls in one register */ 530 if (wm->ctl[n].flags & WM8776_FLAG_STEREO && 531 wm->ctl[n].reg1 == wm->ctl[n].reg2) { 532 val &= ~wm->ctl[n].mask2; 533 val |= regval2 << __ffs(wm->ctl[n].mask2); 534 } 535 snd_wm8776_write(wm, wm->ctl[n].reg1, val); 536 /* stereo controls in different registers */ 537 if (wm->ctl[n].flags & WM8776_FLAG_STEREO && 538 wm->ctl[n].reg1 != wm->ctl[n].reg2) { 539 val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2; 540 val |= regval2 << __ffs(wm->ctl[n].mask2); 541 if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE) 542 val |= WM8776_VOL_UPDATE; 543 snd_wm8776_write(wm, wm->ctl[n].reg2, val); 544 } 545 } 546 547 return 0; 548 } 549 550 static int snd_wm8776_add_control(struct snd_wm8776 *wm, int num) 551 { 552 struct snd_kcontrol_new cont; 553 struct snd_kcontrol *ctl; 554 555 memset(&cont, 0, sizeof(cont)); 556 cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 557 cont.private_value = num; 558 cont.name = wm->ctl[num].name; 559 cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 560 if (wm->ctl[num].flags & WM8776_FLAG_LIM || 561 wm->ctl[num].flags & WM8776_FLAG_ALC) 562 cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 563 cont.tlv.p = NULL; 564 cont.get = snd_wm8776_ctl_get; 565 cont.put = snd_wm8776_ctl_put; 566 567 switch (wm->ctl[num].type) { 568 case SNDRV_CTL_ELEM_TYPE_INTEGER: 569 cont.info = snd_wm8776_volume_info; 570 cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; 571 cont.tlv.p = wm->ctl[num].tlv; 572 break; 573 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 574 wm->ctl[num].max = 1; 575 if (wm->ctl[num].flags & WM8776_FLAG_STEREO) 576 cont.info = snd_ctl_boolean_stereo_info; 577 else 578 cont.info = snd_ctl_boolean_mono_info; 579 break; 580 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 581 cont.info = snd_wm8776_enum_info; 582 break; 583 default: 584 return -EINVAL; 585 } 586 ctl = snd_ctl_new1(&cont, wm); 587 if (!ctl) 588 return -ENOMEM; 589 590 return snd_ctl_add(wm->card, ctl); 591 } 592 593 int snd_wm8776_build_controls(struct snd_wm8776 *wm) 594 { 595 int err, i; 596 597 for (i = 0; i < WM8776_CTL_COUNT; i++) 598 if (wm->ctl[i].name) { 599 err = snd_wm8776_add_control(wm, i); 600 if (err < 0) 601 return err; 602 } 603 604 return 0; 605 } 606