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 strncpy(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 >> 455 void snd_wm8776_set_dac_if(struct snd_wm8776 *wm, u16 dac) >> 456 { >> 457 snd_wm8776_write(wm, WM8776_REG_DACIFCTRL, dac); >> 458 } >> 459 >> 460 void snd_wm8776_set_adc_if(struct snd_wm8776 *wm, u16 adc) >> 461 { >> 462 snd_wm8776_write(wm, WM8776_REG_ADCIFCTRL, adc); >> 463 } >> 464 >> 465 void snd_wm8776_set_master_mode(struct snd_wm8776 *wm, u16 mode) >> 466 { >> 467 snd_wm8776_write(wm, WM8776_REG_MSTRCTRL, mode); >> 468 } >> 469 437 void snd_wm8776_set_power(struct snd_wm8776 *w 470 void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power) 438 { 471 { 439 snd_wm8776_write(wm, WM8776_REG_PWRDOW 472 snd_wm8776_write(wm, WM8776_REG_PWRDOWN, power); 440 } 473 } 441 474 442 void snd_wm8776_volume_restore(struct snd_wm87 475 void snd_wm8776_volume_restore(struct snd_wm8776 *wm) 443 { 476 { 444 u16 val = wm->regs[WM8776_REG_DACRVOL] 477 u16 val = wm->regs[WM8776_REG_DACRVOL]; 445 /* restore volume after MCLK stopped * 478 /* restore volume after MCLK stopped */ 446 snd_wm8776_write(wm, WM8776_REG_DACRVO 479 snd_wm8776_write(wm, WM8776_REG_DACRVOL, val | WM8776_VOL_UPDATE); 447 } 480 } 448 481 449 /* mixer callbacks */ 482 /* mixer callbacks */ 450 483 451 static int snd_wm8776_volume_info(struct snd_k 484 static int snd_wm8776_volume_info(struct snd_kcontrol *kcontrol, 452 struct snd_ 485 struct snd_ctl_elem_info *uinfo) 453 { 486 { 454 struct snd_wm8776 *wm = snd_kcontrol_c 487 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 455 int n = kcontrol->private_value; 488 int n = kcontrol->private_value; 456 489 457 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTE 490 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 458 uinfo->count = (wm->ctl[n].flags & WM8 491 uinfo->count = (wm->ctl[n].flags & WM8776_FLAG_STEREO) ? 2 : 1; 459 uinfo->value.integer.min = wm->ctl[n]. 492 uinfo->value.integer.min = wm->ctl[n].min; 460 uinfo->value.integer.max = wm->ctl[n]. 493 uinfo->value.integer.max = wm->ctl[n].max; 461 494 462 return 0; 495 return 0; 463 } 496 } 464 497 465 static int snd_wm8776_enum_info(struct snd_kco 498 static int snd_wm8776_enum_info(struct snd_kcontrol *kcontrol, 466 struct s 499 struct snd_ctl_elem_info *uinfo) 467 { 500 { 468 struct snd_wm8776 *wm = snd_kcontrol_c 501 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 469 int n = kcontrol->private_value; 502 int n = kcontrol->private_value; 470 503 471 return snd_ctl_enum_info(uinfo, 1, wm- 504 return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max, 472 505 wm->ctl[n].enum_names); 473 } 506 } 474 507 475 static int snd_wm8776_ctl_get(struct snd_kcont 508 static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol, 476 struct snd_c 509 struct snd_ctl_elem_value *ucontrol) 477 { 510 { 478 struct snd_wm8776 *wm = snd_kcontrol_c 511 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 479 int n = kcontrol->private_value; 512 int n = kcontrol->private_value; 480 u16 val1, val2; 513 u16 val1, val2; 481 514 482 if (wm->ctl[n].get) 515 if (wm->ctl[n].get) 483 wm->ctl[n].get(wm, &val1, &val 516 wm->ctl[n].get(wm, &val1, &val2); 484 else { 517 else { 485 val1 = wm->regs[wm->ctl[n].reg 518 val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1; 486 val1 >>= __ffs(wm->ctl[n].mask 519 val1 >>= __ffs(wm->ctl[n].mask1); 487 if (wm->ctl[n].flags & WM8776_ 520 if (wm->ctl[n].flags & WM8776_FLAG_STEREO) { 488 val2 = wm->regs[wm->ct 521 val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2; 489 val2 >>= __ffs(wm->ctl 522 val2 >>= __ffs(wm->ctl[n].mask2); 490 if (wm->ctl[n].flags & 523 if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE) 491 val2 &= ~WM877 524 val2 &= ~WM8776_VOL_UPDATE; 492 } 525 } 493 } 526 } 494 if (wm->ctl[n].flags & WM8776_FLAG_INV 527 if (wm->ctl[n].flags & WM8776_FLAG_INVERT) { 495 val1 = wm->ctl[n].max - (val1 528 val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); 496 if (wm->ctl[n].flags & WM8776_ !! 529 val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); 497 val2 = wm->ctl[n].max << 498 } 530 } 499 ucontrol->value.integer.value[0] = val 531 ucontrol->value.integer.value[0] = val1; 500 if (wm->ctl[n].flags & WM8776_FLAG_STE 532 if (wm->ctl[n].flags & WM8776_FLAG_STEREO) 501 ucontrol->value.integer.value[ 533 ucontrol->value.integer.value[1] = val2; 502 534 503 return 0; 535 return 0; 504 } 536 } 505 537 506 static int snd_wm8776_ctl_put(struct snd_kcont 538 static int snd_wm8776_ctl_put(struct snd_kcontrol *kcontrol, 507 struct snd_c 539 struct snd_ctl_elem_value *ucontrol) 508 { 540 { 509 struct snd_wm8776 *wm = snd_kcontrol_c 541 struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol); 510 int n = kcontrol->private_value; 542 int n = kcontrol->private_value; 511 u16 val, regval1, regval2; 543 u16 val, regval1, regval2; 512 544 513 /* this also works for enum because va !! 545 /* this also works for enum because value is an union */ 514 regval1 = ucontrol->value.integer.valu 546 regval1 = ucontrol->value.integer.value[0]; 515 regval2 = ucontrol->value.integer.valu 547 regval2 = ucontrol->value.integer.value[1]; 516 if (wm->ctl[n].flags & WM8776_FLAG_INV 548 if (wm->ctl[n].flags & WM8776_FLAG_INVERT) { 517 regval1 = wm->ctl[n].max - (re 549 regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min); 518 regval2 = wm->ctl[n].max - (re 550 regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min); 519 } 551 } 520 if (wm->ctl[n].set) 552 if (wm->ctl[n].set) 521 wm->ctl[n].set(wm, regval1, re 553 wm->ctl[n].set(wm, regval1, regval2); 522 else { 554 else { 523 val = wm->regs[wm->ctl[n].reg1 555 val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1; 524 val |= regval1 << __ffs(wm->ct 556 val |= regval1 << __ffs(wm->ctl[n].mask1); 525 /* both stereo controls in one 557 /* both stereo controls in one register */ 526 if (wm->ctl[n].flags & WM8776_ 558 if (wm->ctl[n].flags & WM8776_FLAG_STEREO && 527 wm->ctl[n].reg 559 wm->ctl[n].reg1 == wm->ctl[n].reg2) { 528 val &= ~wm->ctl[n].mas 560 val &= ~wm->ctl[n].mask2; 529 val |= regval2 << __ff 561 val |= regval2 << __ffs(wm->ctl[n].mask2); 530 } 562 } 531 snd_wm8776_write(wm, wm->ctl[n 563 snd_wm8776_write(wm, wm->ctl[n].reg1, val); 532 /* stereo controls in differen 564 /* stereo controls in different registers */ 533 if (wm->ctl[n].flags & WM8776_ 565 if (wm->ctl[n].flags & WM8776_FLAG_STEREO && 534 wm->ctl[n].reg 566 wm->ctl[n].reg1 != wm->ctl[n].reg2) { 535 val = wm->regs[wm->ctl 567 val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2; 536 val |= regval2 << __ff 568 val |= regval2 << __ffs(wm->ctl[n].mask2); 537 if (wm->ctl[n].flags & 569 if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE) 538 val |= WM8776_ 570 val |= WM8776_VOL_UPDATE; 539 snd_wm8776_write(wm, w 571 snd_wm8776_write(wm, wm->ctl[n].reg2, val); 540 } 572 } 541 } 573 } 542 574 543 return 0; 575 return 0; 544 } 576 } 545 577 546 static int snd_wm8776_add_control(struct snd_w 578 static int snd_wm8776_add_control(struct snd_wm8776 *wm, int num) 547 { 579 { 548 struct snd_kcontrol_new cont; 580 struct snd_kcontrol_new cont; 549 struct snd_kcontrol *ctl; 581 struct snd_kcontrol *ctl; 550 582 551 memset(&cont, 0, sizeof(cont)); 583 memset(&cont, 0, sizeof(cont)); 552 cont.iface = SNDRV_CTL_ELEM_IFACE_MIXE 584 cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 553 cont.private_value = num; 585 cont.private_value = num; 554 cont.name = wm->ctl[num].name; 586 cont.name = wm->ctl[num].name; 555 cont.access = SNDRV_CTL_ELEM_ACCESS_RE 587 cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 556 if (wm->ctl[num].flags & WM8776_FLAG_L 588 if (wm->ctl[num].flags & WM8776_FLAG_LIM || 557 wm->ctl[num].flags & WM8776_FLAG_A 589 wm->ctl[num].flags & WM8776_FLAG_ALC) 558 cont.access |= SNDRV_CTL_ELEM_ 590 cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 559 cont.tlv.p = NULL; 591 cont.tlv.p = NULL; 560 cont.get = snd_wm8776_ctl_get; 592 cont.get = snd_wm8776_ctl_get; 561 cont.put = snd_wm8776_ctl_put; 593 cont.put = snd_wm8776_ctl_put; 562 594 563 switch (wm->ctl[num].type) { 595 switch (wm->ctl[num].type) { 564 case SNDRV_CTL_ELEM_TYPE_INTEGER: 596 case SNDRV_CTL_ELEM_TYPE_INTEGER: 565 cont.info = snd_wm8776_volume_ 597 cont.info = snd_wm8776_volume_info; 566 cont.access |= SNDRV_CTL_ELEM_ 598 cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; 567 cont.tlv.p = wm->ctl[num].tlv; 599 cont.tlv.p = wm->ctl[num].tlv; 568 break; 600 break; 569 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 601 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 570 wm->ctl[num].max = 1; 602 wm->ctl[num].max = 1; 571 if (wm->ctl[num].flags & WM877 603 if (wm->ctl[num].flags & WM8776_FLAG_STEREO) 572 cont.info = snd_ctl_bo 604 cont.info = snd_ctl_boolean_stereo_info; 573 else 605 else 574 cont.info = snd_ctl_bo 606 cont.info = snd_ctl_boolean_mono_info; 575 break; 607 break; 576 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 608 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 577 cont.info = snd_wm8776_enum_in 609 cont.info = snd_wm8776_enum_info; 578 break; 610 break; 579 default: 611 default: 580 return -EINVAL; 612 return -EINVAL; 581 } 613 } 582 ctl = snd_ctl_new1(&cont, wm); 614 ctl = snd_ctl_new1(&cont, wm); 583 if (!ctl) 615 if (!ctl) 584 return -ENOMEM; 616 return -ENOMEM; 585 617 586 return snd_ctl_add(wm->card, ctl); 618 return snd_ctl_add(wm->card, ctl); 587 } 619 } 588 620 589 int snd_wm8776_build_controls(struct snd_wm877 621 int snd_wm8776_build_controls(struct snd_wm8776 *wm) 590 { 622 { 591 int err, i; 623 int err, i; 592 624 593 for (i = 0; i < WM8776_CTL_COUNT; i++) 625 for (i = 0; i < WM8776_CTL_COUNT; i++) 594 if (wm->ctl[i].name) { 626 if (wm->ctl[i].name) { 595 err = snd_wm8776_add_c 627 err = snd_wm8776_add_control(wm, i); 596 if (err < 0) 628 if (err < 0) 597 return err; 629 return err; 598 } 630 } 599 631 600 return 0; 632 return 0; 601 } 633 } 602 634
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.