1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ALSA driver for ICEnsemble VT17xx
5 * Lowlevel functions for WM8776 codec
7 * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
10 #include <linux/delay.h>
11 #include <sound/core.h>
12 #include <sound/control.h>
13 #include <sound/tlv.h>
16 /* low-level access */
18 static void snd_wm8776_write(struct snd_wm8776
*wm
, u16 addr
, u16 data
)
20 u8 bus_addr
= addr
<< 1 | data
>> 8; /* addr + 9th data bit */
21 u8 bus_data
= data
& 0xff; /* remaining 8 data bits */
23 if (addr
< WM8776_REG_RESET
)
24 wm
->regs
[addr
] = data
;
25 wm
->ops
.write(wm
, bus_addr
, bus_data
);
28 /* register-level functions */
30 static void snd_wm8776_activate_ctl(struct snd_wm8776
*wm
,
34 struct snd_card
*card
= wm
->card
;
35 struct snd_kcontrol
*kctl
;
36 struct snd_kcontrol_volatile
*vd
;
37 unsigned int index_offset
;
39 kctl
= snd_ctl_find_id_mixer(card
, ctl_name
);
42 index_offset
= snd_ctl_get_ioff(kctl
, &kctl
->id
);
43 vd
= &kctl
->vd
[index_offset
];
45 vd
->access
&= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE
;
47 vd
->access
|= SNDRV_CTL_ELEM_ACCESS_INACTIVE
;
48 snd_ctl_notify(card
, SNDRV_CTL_EVENT_MASK_INFO
, &kctl
->id
);
51 static void snd_wm8776_update_agc_ctl(struct snd_wm8776
*wm
)
53 int i
, flags_on
= 0, flags_off
= 0;
55 switch (wm
->agc_mode
) {
57 flags_off
= WM8776_FLAG_LIM
| WM8776_FLAG_ALC
;
60 flags_off
= WM8776_FLAG_ALC
;
61 flags_on
= WM8776_FLAG_LIM
;
63 case WM8776_AGC_ALC_R
:
64 case WM8776_AGC_ALC_L
:
65 case WM8776_AGC_ALC_STEREO
:
66 flags_off
= WM8776_FLAG_LIM
;
67 flags_on
= WM8776_FLAG_ALC
;
71 for (i
= 0; i
< WM8776_CTL_COUNT
; i
++)
72 if (wm
->ctl
[i
].flags
& flags_off
)
73 snd_wm8776_activate_ctl(wm
, wm
->ctl
[i
].name
, false);
74 else if (wm
->ctl
[i
].flags
& flags_on
)
75 snd_wm8776_activate_ctl(wm
, wm
->ctl
[i
].name
, true);
78 static void snd_wm8776_set_agc(struct snd_wm8776
*wm
, u16 agc
, u16 nothing
)
80 u16 alc1
= wm
->regs
[WM8776_REG_ALCCTRL1
] & ~WM8776_ALC1_LCT_MASK
;
81 u16 alc2
= wm
->regs
[WM8776_REG_ALCCTRL2
] & ~WM8776_ALC2_LCEN
;
85 wm
->agc_mode
= WM8776_AGC_OFF
;
88 alc2
|= WM8776_ALC2_LCEN
;
89 wm
->agc_mode
= WM8776_AGC_LIM
;
91 case 2: /* ALC Right */
92 alc1
|= WM8776_ALC1_LCSEL_ALCR
;
93 alc2
|= WM8776_ALC2_LCEN
;
94 wm
->agc_mode
= WM8776_AGC_ALC_R
;
96 case 3: /* ALC Left */
97 alc1
|= WM8776_ALC1_LCSEL_ALCL
;
98 alc2
|= WM8776_ALC2_LCEN
;
99 wm
->agc_mode
= WM8776_AGC_ALC_L
;
101 case 4: /* ALC Stereo */
102 alc1
|= WM8776_ALC1_LCSEL_ALCSTEREO
;
103 alc2
|= WM8776_ALC2_LCEN
;
104 wm
->agc_mode
= WM8776_AGC_ALC_STEREO
;
107 snd_wm8776_write(wm
, WM8776_REG_ALCCTRL1
, alc1
);
108 snd_wm8776_write(wm
, WM8776_REG_ALCCTRL2
, alc2
);
109 snd_wm8776_update_agc_ctl(wm
);
112 static void snd_wm8776_get_agc(struct snd_wm8776
*wm
, u16
*mode
, u16
*nothing
)
114 *mode
= wm
->agc_mode
;
119 static const DECLARE_TLV_DB_SCALE(wm8776_hp_tlv
, -7400, 100, 1);
120 static const DECLARE_TLV_DB_SCALE(wm8776_dac_tlv
, -12750, 50, 1);
121 static const DECLARE_TLV_DB_SCALE(wm8776_adc_tlv
, -10350, 50, 1);
122 static const DECLARE_TLV_DB_SCALE(wm8776_lct_tlv
, -1600, 100, 0);
123 static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_tlv
, 0, 400, 0);
124 static const DECLARE_TLV_DB_SCALE(wm8776_ngth_tlv
, -7800, 600, 0);
125 static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_tlv
, -1200, 100, 0);
126 static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_tlv
, -2100, 400, 0);
128 static const struct snd_wm8776_ctl snd_wm8776_default_ctl
[WM8776_CTL_COUNT
] = {
129 [WM8776_CTL_DAC_VOL
] = {
130 .name
= "Master Playback Volume",
131 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
132 .tlv
= wm8776_dac_tlv
,
133 .reg1
= WM8776_REG_DACLVOL
,
134 .reg2
= WM8776_REG_DACRVOL
,
135 .mask1
= WM8776_DACVOL_MASK
,
136 .mask2
= WM8776_DACVOL_MASK
,
138 .flags
= WM8776_FLAG_STEREO
| WM8776_FLAG_VOL_UPDATE
,
140 [WM8776_CTL_DAC_SW
] = {
141 .name
= "Master Playback Switch",
142 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
143 .reg1
= WM8776_REG_DACCTRL1
,
144 .reg2
= WM8776_REG_DACCTRL1
,
145 .mask1
= WM8776_DAC_PL_LL
,
146 .mask2
= WM8776_DAC_PL_RR
,
147 .flags
= WM8776_FLAG_STEREO
,
149 [WM8776_CTL_DAC_ZC_SW
] = {
150 .name
= "Master Zero Cross Detect Playback Switch",
151 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
152 .reg1
= WM8776_REG_DACCTRL1
,
153 .mask1
= WM8776_DAC_DZCEN
,
155 [WM8776_CTL_HP_VOL
] = {
156 .name
= "Headphone Playback Volume",
157 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
158 .tlv
= wm8776_hp_tlv
,
159 .reg1
= WM8776_REG_HPLVOL
,
160 .reg2
= WM8776_REG_HPRVOL
,
161 .mask1
= WM8776_HPVOL_MASK
,
162 .mask2
= WM8776_HPVOL_MASK
,
165 .flags
= WM8776_FLAG_STEREO
| WM8776_FLAG_VOL_UPDATE
,
167 [WM8776_CTL_HP_SW
] = {
168 .name
= "Headphone Playback Switch",
169 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
170 .reg1
= WM8776_REG_PWRDOWN
,
171 .mask1
= WM8776_PWR_HPPD
,
172 .flags
= WM8776_FLAG_INVERT
,
174 [WM8776_CTL_HP_ZC_SW
] = {
175 .name
= "Headphone Zero Cross Detect Playback Switch",
176 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
177 .reg1
= WM8776_REG_HPLVOL
,
178 .reg2
= WM8776_REG_HPRVOL
,
179 .mask1
= WM8776_VOL_HPZCEN
,
180 .mask2
= WM8776_VOL_HPZCEN
,
181 .flags
= WM8776_FLAG_STEREO
,
183 [WM8776_CTL_AUX_SW
] = {
184 .name
= "AUX Playback Switch",
185 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
186 .reg1
= WM8776_REG_OUTMUX
,
187 .mask1
= WM8776_OUTMUX_AUX
,
189 [WM8776_CTL_BYPASS_SW
] = {
190 .name
= "Bypass Playback Switch",
191 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
192 .reg1
= WM8776_REG_OUTMUX
,
193 .mask1
= WM8776_OUTMUX_BYPASS
,
195 [WM8776_CTL_DAC_IZD_SW
] = {
196 .name
= "Infinite Zero Detect Playback Switch",
197 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
198 .reg1
= WM8776_REG_DACCTRL1
,
199 .mask1
= WM8776_DAC_IZD
,
201 [WM8776_CTL_PHASE_SW
] = {
202 .name
= "Phase Invert Playback Switch",
203 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
204 .reg1
= WM8776_REG_PHASESWAP
,
205 .reg2
= WM8776_REG_PHASESWAP
,
206 .mask1
= WM8776_PHASE_INVERTL
,
207 .mask2
= WM8776_PHASE_INVERTR
,
208 .flags
= WM8776_FLAG_STEREO
,
210 [WM8776_CTL_DEEMPH_SW
] = {
211 .name
= "Deemphasis Playback Switch",
212 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
213 .reg1
= WM8776_REG_DACCTRL2
,
214 .mask1
= WM8776_DAC2_DEEMPH
,
216 [WM8776_CTL_ADC_VOL
] = {
217 .name
= "Input Capture Volume",
218 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
219 .tlv
= wm8776_adc_tlv
,
220 .reg1
= WM8776_REG_ADCLVOL
,
221 .reg2
= WM8776_REG_ADCRVOL
,
222 .mask1
= WM8776_ADC_GAIN_MASK
,
223 .mask2
= WM8776_ADC_GAIN_MASK
,
225 .flags
= WM8776_FLAG_STEREO
| WM8776_FLAG_VOL_UPDATE
,
227 [WM8776_CTL_ADC_SW
] = {
228 .name
= "Input Capture Switch",
229 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
230 .reg1
= WM8776_REG_ADCMUX
,
231 .reg2
= WM8776_REG_ADCMUX
,
232 .mask1
= WM8776_ADC_MUTEL
,
233 .mask2
= WM8776_ADC_MUTER
,
234 .flags
= WM8776_FLAG_STEREO
| WM8776_FLAG_INVERT
,
236 [WM8776_CTL_INPUT1_SW
] = {
237 .name
= "AIN1 Capture Switch",
238 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
239 .reg1
= WM8776_REG_ADCMUX
,
240 .mask1
= WM8776_ADC_MUX_AIN1
,
242 [WM8776_CTL_INPUT2_SW
] = {
243 .name
= "AIN2 Capture Switch",
244 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
245 .reg1
= WM8776_REG_ADCMUX
,
246 .mask1
= WM8776_ADC_MUX_AIN2
,
248 [WM8776_CTL_INPUT3_SW
] = {
249 .name
= "AIN3 Capture Switch",
250 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
251 .reg1
= WM8776_REG_ADCMUX
,
252 .mask1
= WM8776_ADC_MUX_AIN3
,
254 [WM8776_CTL_INPUT4_SW
] = {
255 .name
= "AIN4 Capture Switch",
256 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
257 .reg1
= WM8776_REG_ADCMUX
,
258 .mask1
= WM8776_ADC_MUX_AIN4
,
260 [WM8776_CTL_INPUT5_SW
] = {
261 .name
= "AIN5 Capture Switch",
262 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
263 .reg1
= WM8776_REG_ADCMUX
,
264 .mask1
= WM8776_ADC_MUX_AIN5
,
266 [WM8776_CTL_AGC_SEL
] = {
267 .name
= "AGC Select Capture Enum",
268 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
269 .enum_names
= { "Off", "Limiter", "ALC Right", "ALC Left",
271 .max
= 5, /* .enum_names item count */
272 .set
= snd_wm8776_set_agc
,
273 .get
= snd_wm8776_get_agc
,
275 [WM8776_CTL_LIM_THR
] = {
276 .name
= "Limiter Threshold Capture Volume",
277 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
278 .tlv
= wm8776_lct_tlv
,
279 .reg1
= WM8776_REG_ALCCTRL1
,
280 .mask1
= WM8776_ALC1_LCT_MASK
,
282 .flags
= WM8776_FLAG_LIM
,
284 [WM8776_CTL_LIM_ATK
] = {
285 .name
= "Limiter Attack Time Capture Enum",
286 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
287 .enum_names
= { "0.25 ms", "0.5 ms", "1 ms", "2 ms", "4 ms",
288 "8 ms", "16 ms", "32 ms", "64 ms", "128 ms", "256 ms" },
289 .max
= 11, /* .enum_names item count */
290 .reg1
= WM8776_REG_ALCCTRL3
,
291 .mask1
= WM8776_ALC3_ATK_MASK
,
292 .flags
= WM8776_FLAG_LIM
,
294 [WM8776_CTL_LIM_DCY
] = {
295 .name
= "Limiter Decay Time Capture Enum",
296 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
297 .enum_names
= { "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
298 "19.2 ms", "38.4 ms", "76.8 ms", "154 ms", "307 ms",
299 "614 ms", "1.23 s" },
300 .max
= 11, /* .enum_names item count */
301 .reg1
= WM8776_REG_ALCCTRL3
,
302 .mask1
= WM8776_ALC3_DCY_MASK
,
303 .flags
= WM8776_FLAG_LIM
,
305 [WM8776_CTL_LIM_TRANWIN
] = {
306 .name
= "Limiter Transient Window Capture Enum",
307 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
308 .enum_names
= { "0 us", "62.5 us", "125 us", "250 us", "500 us",
309 "1 ms", "2 ms", "4 ms" },
310 .max
= 8, /* .enum_names item count */
311 .reg1
= WM8776_REG_LIMITER
,
312 .mask1
= WM8776_LIM_TRANWIN_MASK
,
313 .flags
= WM8776_FLAG_LIM
,
315 [WM8776_CTL_LIM_MAXATTN
] = {
316 .name
= "Limiter Maximum Attenuation Capture Volume",
317 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
318 .tlv
= wm8776_maxatten_lim_tlv
,
319 .reg1
= WM8776_REG_LIMITER
,
320 .mask1
= WM8776_LIM_MAXATTEN_MASK
,
323 .flags
= WM8776_FLAG_LIM
| WM8776_FLAG_INVERT
,
325 [WM8776_CTL_ALC_TGT
] = {
326 .name
= "ALC Target Level Capture Volume",
327 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
328 .tlv
= wm8776_lct_tlv
,
329 .reg1
= WM8776_REG_ALCCTRL1
,
330 .mask1
= WM8776_ALC1_LCT_MASK
,
332 .flags
= WM8776_FLAG_ALC
,
334 [WM8776_CTL_ALC_ATK
] = {
335 .name
= "ALC Attack Time Capture Enum",
336 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
337 .enum_names
= { "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
338 "134 ms", "269 ms", "538 ms", "1.08 s", "2.15 s",
340 .max
= 11, /* .enum_names item count */
341 .reg1
= WM8776_REG_ALCCTRL3
,
342 .mask1
= WM8776_ALC3_ATK_MASK
,
343 .flags
= WM8776_FLAG_ALC
,
345 [WM8776_CTL_ALC_DCY
] = {
346 .name
= "ALC Decay Time Capture Enum",
347 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
348 .enum_names
= { "33.5 ms", "67.0 ms", "134 ms", "268 ms",
349 "536 ms", "1.07 s", "2.14 s", "4.29 s", "8.58 s",
350 "17.2 s", "34.3 s" },
351 .max
= 11, /* .enum_names item count */
352 .reg1
= WM8776_REG_ALCCTRL3
,
353 .mask1
= WM8776_ALC3_DCY_MASK
,
354 .flags
= WM8776_FLAG_ALC
,
356 [WM8776_CTL_ALC_MAXGAIN
] = {
357 .name
= "ALC Maximum Gain Capture Volume",
358 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
359 .tlv
= wm8776_maxgain_tlv
,
360 .reg1
= WM8776_REG_ALCCTRL1
,
361 .mask1
= WM8776_ALC1_MAXGAIN_MASK
,
364 .flags
= WM8776_FLAG_ALC
,
366 [WM8776_CTL_ALC_MAXATTN
] = {
367 .name
= "ALC Maximum Attenuation Capture Volume",
368 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
369 .tlv
= wm8776_maxatten_alc_tlv
,
370 .reg1
= WM8776_REG_LIMITER
,
371 .mask1
= WM8776_LIM_MAXATTEN_MASK
,
374 .flags
= WM8776_FLAG_ALC
| WM8776_FLAG_INVERT
,
376 [WM8776_CTL_ALC_HLD
] = {
377 .name
= "ALC Hold Time Capture Enum",
378 .type
= SNDRV_CTL_ELEM_TYPE_ENUMERATED
,
379 .enum_names
= { "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
380 "21.3 ms", "42.7 ms", "85.3 ms", "171 ms", "341 ms",
381 "683 ms", "1.37 s", "2.73 s", "5.46 s", "10.9 s",
382 "21.8 s", "43.7 s" },
383 .max
= 16, /* .enum_names item count */
384 .reg1
= WM8776_REG_ALCCTRL2
,
385 .mask1
= WM8776_ALC2_HOLD_MASK
,
386 .flags
= WM8776_FLAG_ALC
,
388 [WM8776_CTL_NGT_SW
] = {
389 .name
= "Noise Gate Capture Switch",
390 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
391 .reg1
= WM8776_REG_NOISEGATE
,
392 .mask1
= WM8776_NGAT_ENABLE
,
393 .flags
= WM8776_FLAG_ALC
,
395 [WM8776_CTL_NGT_THR
] = {
396 .name
= "Noise Gate Threshold Capture Volume",
397 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
398 .tlv
= wm8776_ngth_tlv
,
399 .reg1
= WM8776_REG_NOISEGATE
,
400 .mask1
= WM8776_NGAT_THR_MASK
,
402 .flags
= WM8776_FLAG_ALC
,
406 /* exported functions */
408 void snd_wm8776_init(struct snd_wm8776
*wm
)
411 static const u16 default_values
[] = {
414 0x000, 0x090, 0x000, 0x000,
416 0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
417 0x032, 0x000, 0x0a6, 0x001, 0x001
420 memcpy(wm
->ctl
, snd_wm8776_default_ctl
, sizeof(wm
->ctl
));
422 snd_wm8776_write(wm
, WM8776_REG_RESET
, 0x00); /* reset */
425 for (i
= 0; i
< ARRAY_SIZE(default_values
); i
++)
426 snd_wm8776_write(wm
, i
, default_values
[i
]);
429 void snd_wm8776_resume(struct snd_wm8776
*wm
)
433 for (i
= 0; i
< WM8776_REG_COUNT
; i
++)
434 snd_wm8776_write(wm
, i
, wm
->regs
[i
]);
437 void snd_wm8776_set_power(struct snd_wm8776
*wm
, u16 power
)
439 snd_wm8776_write(wm
, WM8776_REG_PWRDOWN
, power
);
442 void snd_wm8776_volume_restore(struct snd_wm8776
*wm
)
444 u16 val
= wm
->regs
[WM8776_REG_DACRVOL
];
445 /* restore volume after MCLK stopped */
446 snd_wm8776_write(wm
, WM8776_REG_DACRVOL
, val
| WM8776_VOL_UPDATE
);
449 /* mixer callbacks */
451 static int snd_wm8776_volume_info(struct snd_kcontrol
*kcontrol
,
452 struct snd_ctl_elem_info
*uinfo
)
454 struct snd_wm8776
*wm
= snd_kcontrol_chip(kcontrol
);
455 int n
= kcontrol
->private_value
;
457 uinfo
->type
= SNDRV_CTL_ELEM_TYPE_INTEGER
;
458 uinfo
->count
= (wm
->ctl
[n
].flags
& WM8776_FLAG_STEREO
) ? 2 : 1;
459 uinfo
->value
.integer
.min
= wm
->ctl
[n
].min
;
460 uinfo
->value
.integer
.max
= wm
->ctl
[n
].max
;
465 static int snd_wm8776_enum_info(struct snd_kcontrol
*kcontrol
,
466 struct snd_ctl_elem_info
*uinfo
)
468 struct snd_wm8776
*wm
= snd_kcontrol_chip(kcontrol
);
469 int n
= kcontrol
->private_value
;
471 return snd_ctl_enum_info(uinfo
, 1, wm
->ctl
[n
].max
,
472 wm
->ctl
[n
].enum_names
);
475 static int snd_wm8776_ctl_get(struct snd_kcontrol
*kcontrol
,
476 struct snd_ctl_elem_value
*ucontrol
)
478 struct snd_wm8776
*wm
= snd_kcontrol_chip(kcontrol
);
479 int n
= kcontrol
->private_value
;
483 wm
->ctl
[n
].get(wm
, &val1
, &val2
);
485 val1
= wm
->regs
[wm
->ctl
[n
].reg1
] & wm
->ctl
[n
].mask1
;
486 val1
>>= __ffs(wm
->ctl
[n
].mask1
);
487 if (wm
->ctl
[n
].flags
& WM8776_FLAG_STEREO
) {
488 val2
= wm
->regs
[wm
->ctl
[n
].reg2
] & wm
->ctl
[n
].mask2
;
489 val2
>>= __ffs(wm
->ctl
[n
].mask2
);
490 if (wm
->ctl
[n
].flags
& WM8776_FLAG_VOL_UPDATE
)
491 val2
&= ~WM8776_VOL_UPDATE
;
494 if (wm
->ctl
[n
].flags
& WM8776_FLAG_INVERT
) {
495 val1
= wm
->ctl
[n
].max
- (val1
- wm
->ctl
[n
].min
);
496 if (wm
->ctl
[n
].flags
& WM8776_FLAG_STEREO
)
497 val2
= wm
->ctl
[n
].max
- (val2
- wm
->ctl
[n
].min
);
499 ucontrol
->value
.integer
.value
[0] = val1
;
500 if (wm
->ctl
[n
].flags
& WM8776_FLAG_STEREO
)
501 ucontrol
->value
.integer
.value
[1] = val2
;
506 static int snd_wm8776_ctl_put(struct snd_kcontrol
*kcontrol
,
507 struct snd_ctl_elem_value
*ucontrol
)
509 struct snd_wm8776
*wm
= snd_kcontrol_chip(kcontrol
);
510 int n
= kcontrol
->private_value
;
511 u16 val
, regval1
, regval2
;
513 /* this also works for enum because value is a union */
514 regval1
= ucontrol
->value
.integer
.value
[0];
515 regval2
= ucontrol
->value
.integer
.value
[1];
516 if (wm
->ctl
[n
].flags
& WM8776_FLAG_INVERT
) {
517 regval1
= wm
->ctl
[n
].max
- (regval1
- wm
->ctl
[n
].min
);
518 regval2
= wm
->ctl
[n
].max
- (regval2
- wm
->ctl
[n
].min
);
521 wm
->ctl
[n
].set(wm
, regval1
, regval2
);
523 val
= wm
->regs
[wm
->ctl
[n
].reg1
] & ~wm
->ctl
[n
].mask1
;
524 val
|= regval1
<< __ffs(wm
->ctl
[n
].mask1
);
525 /* both stereo controls in one register */
526 if (wm
->ctl
[n
].flags
& WM8776_FLAG_STEREO
&&
527 wm
->ctl
[n
].reg1
== wm
->ctl
[n
].reg2
) {
528 val
&= ~wm
->ctl
[n
].mask2
;
529 val
|= regval2
<< __ffs(wm
->ctl
[n
].mask2
);
531 snd_wm8776_write(wm
, wm
->ctl
[n
].reg1
, val
);
532 /* stereo controls in different registers */
533 if (wm
->ctl
[n
].flags
& WM8776_FLAG_STEREO
&&
534 wm
->ctl
[n
].reg1
!= wm
->ctl
[n
].reg2
) {
535 val
= wm
->regs
[wm
->ctl
[n
].reg2
] & ~wm
->ctl
[n
].mask2
;
536 val
|= regval2
<< __ffs(wm
->ctl
[n
].mask2
);
537 if (wm
->ctl
[n
].flags
& WM8776_FLAG_VOL_UPDATE
)
538 val
|= WM8776_VOL_UPDATE
;
539 snd_wm8776_write(wm
, wm
->ctl
[n
].reg2
, val
);
546 static int snd_wm8776_add_control(struct snd_wm8776
*wm
, int num
)
548 struct snd_kcontrol_new cont
;
549 struct snd_kcontrol
*ctl
;
551 memset(&cont
, 0, sizeof(cont
));
552 cont
.iface
= SNDRV_CTL_ELEM_IFACE_MIXER
;
553 cont
.private_value
= num
;
554 cont
.name
= wm
->ctl
[num
].name
;
555 cont
.access
= SNDRV_CTL_ELEM_ACCESS_READWRITE
;
556 if (wm
->ctl
[num
].flags
& WM8776_FLAG_LIM
||
557 wm
->ctl
[num
].flags
& WM8776_FLAG_ALC
)
558 cont
.access
|= SNDRV_CTL_ELEM_ACCESS_INACTIVE
;
560 cont
.get
= snd_wm8776_ctl_get
;
561 cont
.put
= snd_wm8776_ctl_put
;
563 switch (wm
->ctl
[num
].type
) {
564 case SNDRV_CTL_ELEM_TYPE_INTEGER
:
565 cont
.info
= snd_wm8776_volume_info
;
566 cont
.access
|= SNDRV_CTL_ELEM_ACCESS_TLV_READ
;
567 cont
.tlv
.p
= wm
->ctl
[num
].tlv
;
569 case SNDRV_CTL_ELEM_TYPE_BOOLEAN
:
570 wm
->ctl
[num
].max
= 1;
571 if (wm
->ctl
[num
].flags
& WM8776_FLAG_STEREO
)
572 cont
.info
= snd_ctl_boolean_stereo_info
;
574 cont
.info
= snd_ctl_boolean_mono_info
;
576 case SNDRV_CTL_ELEM_TYPE_ENUMERATED
:
577 cont
.info
= snd_wm8776_enum_info
;
582 ctl
= snd_ctl_new1(&cont
, wm
);
586 return snd_ctl_add(wm
->card
, ctl
);
589 int snd_wm8776_build_controls(struct snd_wm8776
*wm
)
593 for (i
= 0; i
< WM8776_CTL_COUNT
; i
++)
594 if (wm
->ctl
[i
].name
) {
595 err
= snd_wm8776_add_control(wm
, i
);