~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/pci/ice1712/wm8776.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

Diff markup

Differences between /sound/pci/ice1712/wm8776.c (Version linux-6.12-rc7) and /sound/pci/ice1712/wm8776.c (Version linux-6.6.60)


  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php