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