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