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