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