2 * ALSA driver for ICEnsemble VT17xx
4 * Lowlevel functions for WM8766 codec
6 * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
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.
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.
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
24 #include <linux/delay.h>
25 #include <sound/core.h>
26 #include <sound/control.h>
27 #include <sound/tlv.h>
30 /* low-level access */
32 static void snd_wm8766_write(struct snd_wm8766
*wm
, u16 addr
, u16 data
)
34 if (addr
< WM8766_REG_RESET
)
35 wm
->regs
[addr
] = data
;
36 wm
->ops
.write(wm
, addr
, data
);
41 static const DECLARE_TLV_DB_SCALE(wm8766_tlv
, -12750, 50, 1);
43 static struct snd_wm8766_ctl snd_wm8766_default_ctl
[WM8766_CTL_COUNT
] = {
44 [WM8766_CTL_CH1_VOL
] = {
45 .name
= "Channel 1 Playback Volume",
46 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
48 .reg1
= WM8766_REG_DACL1
,
49 .reg2
= WM8766_REG_DACR1
,
50 .mask1
= WM8766_VOL_MASK
,
51 .mask2
= WM8766_VOL_MASK
,
53 .flags
= WM8766_FLAG_STEREO
| WM8766_FLAG_VOL_UPDATE
,
55 [WM8766_CTL_CH2_VOL
] = {
56 .name
= "Channel 2 Playback Volume",
57 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
59 .reg1
= WM8766_REG_DACL2
,
60 .reg2
= WM8766_REG_DACR2
,
61 .mask1
= WM8766_VOL_MASK
,
62 .mask2
= WM8766_VOL_MASK
,
64 .flags
= WM8766_FLAG_STEREO
| WM8766_FLAG_VOL_UPDATE
,
66 [WM8766_CTL_CH3_VOL
] = {
67 .name
= "Channel 3 Playback Volume",
68 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
70 .reg1
= WM8766_REG_DACL3
,
71 .reg2
= WM8766_REG_DACR3
,
72 .mask1
= WM8766_VOL_MASK
,
73 .mask2
= WM8766_VOL_MASK
,
75 .flags
= WM8766_FLAG_STEREO
| WM8766_FLAG_VOL_UPDATE
,
77 [WM8766_CTL_CH1_SW
] = {
78 .name
= "Channel 1 Playback Switch",
79 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
80 .reg1
= WM8766_REG_DACCTRL2
,
81 .mask1
= WM8766_DAC2_MUTE1
,
82 .flags
= WM8766_FLAG_INVERT
,
84 [WM8766_CTL_CH2_SW
] = {
85 .name
= "Channel 2 Playback Switch",
86 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
87 .reg1
= WM8766_REG_DACCTRL2
,
88 .mask1
= WM8766_DAC2_MUTE2
,
89 .flags
= WM8766_FLAG_INVERT
,
91 [WM8766_CTL_CH3_SW
] = {
92 .name
= "Channel 3 Playback Switch",
93 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
94 .reg1
= WM8766_REG_DACCTRL2
,
95 .mask1
= WM8766_DAC2_MUTE3
,
96 .flags
= WM8766_FLAG_INVERT
,
98 [WM8766_CTL_PHASE1_SW
] = {
99 .name
= "Channel 1 Phase Invert Playback Switch",
100 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
101 .reg1
= WM8766_REG_IFCTRL
,
102 .mask1
= WM8766_PHASE_INVERT1
,
104 [WM8766_CTL_PHASE2_SW
] = {
105 .name
= "Channel 2 Phase Invert Playback Switch",
106 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
107 .reg1
= WM8766_REG_IFCTRL
,
108 .mask1
= WM8766_PHASE_INVERT2
,
110 [WM8766_CTL_PHASE3_SW
] = {
111 .name
= "Channel 3 Phase Invert Playback Switch",
112 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
113 .reg1
= WM8766_REG_IFCTRL
,
114 .mask1
= WM8766_PHASE_INVERT3
,
116 [WM8766_CTL_DEEMPH1_SW
] = {
117 .name
= "Channel 1 Deemphasis Playback Switch",
118 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
119 .reg1
= WM8766_REG_DACCTRL2
,
120 .mask1
= WM8766_DAC2_DEEMP1
,
122 [WM8766_CTL_DEEMPH2_SW
] = {
123 .name
= "Channel 2 Deemphasis Playback Switch",
124 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
125 .reg1
= WM8766_REG_DACCTRL2
,
126 .mask1
= WM8766_DAC2_DEEMP2
,
128 [WM8766_CTL_DEEMPH3_SW
] = {
129 .name
= "Channel 3 Deemphasis Playback Switch",
130 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
131 .reg1
= WM8766_REG_DACCTRL2
,
132 .mask1
= WM8766_DAC2_DEEMP3
,
134 [WM8766_CTL_IZD_SW
] = {
135 .name
= "Infinite Zero Detect Playback Switch",
136 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
137 .reg1
= WM8766_REG_DACCTRL1
,
138 .mask1
= WM8766_DAC_IZD
,
140 [WM8766_CTL_ZC_SW
] = {
141 .name
= "Zero Cross Detect Playback Switch",
142 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
143 .reg1
= WM8766_REG_DACCTRL2
,
144 .mask1
= WM8766_DAC2_ZCD
,
145 .flags
= WM8766_FLAG_INVERT
,
149 /* exported functions */
151 void snd_wm8766_init(struct snd_wm8766
*wm
)
154 static const u16 default_values
[] = {
157 0x000, 0x100, 0x000, 0x100, 0x000,
161 memcpy(wm
->ctl
, snd_wm8766_default_ctl
, sizeof(wm
->ctl
));
163 snd_wm8766_write(wm
, WM8766_REG_RESET
, 0x00); /* reset */
166 for (i
= 0; i
< ARRAY_SIZE(default_values
); i
++)
167 snd_wm8766_write(wm
, i
, default_values
[i
]);
170 void snd_wm8766_resume(struct snd_wm8766
*wm
)
174 for (i
= 0; i
< WM8766_REG_COUNT
; i
++)
175 snd_wm8766_write(wm
, i
, wm
->regs
[i
]);
178 void snd_wm8766_set_if(struct snd_wm8766
*wm
, u16 dac
)
180 u16 val
= wm
->regs
[WM8766_REG_IFCTRL
] & ~WM8766_IF_MASK
;
182 dac
&= WM8766_IF_MASK
;
183 snd_wm8766_write(wm
, WM8766_REG_IFCTRL
, val
| dac
);
186 void snd_wm8766_set_master_mode(struct snd_wm8766
*wm
, u16 mode
)
188 u16 val
= wm
->regs
[WM8766_REG_DACCTRL3
] & ~WM8766_DAC3_MSTR_MASK
;
190 mode
&= WM8766_DAC3_MSTR_MASK
;
191 snd_wm8766_write(wm
, WM8766_REG_DACCTRL3
, val
| mode
);
194 void snd_wm8766_set_power(struct snd_wm8766
*wm
, u16 power
)
196 u16 val
= wm
->regs
[WM8766_REG_DACCTRL3
] & ~WM8766_DAC3_POWER_MASK
;
198 power
&= WM8766_DAC3_POWER_MASK
;
199 snd_wm8766_write(wm
, WM8766_REG_DACCTRL3
, val
| power
);
202 void snd_wm8766_volume_restore(struct snd_wm8766
*wm
)
204 u16 val
= wm
->regs
[WM8766_REG_DACR1
];
205 /* restore volume after MCLK stopped */
206 snd_wm8766_write(wm
, WM8766_REG_DACR1
, val
| WM8766_VOL_UPDATE
);
209 /* mixer callbacks */
211 static int snd_wm8766_volume_info(struct snd_kcontrol
*kcontrol
,
212 struct snd_ctl_elem_info
*uinfo
)
214 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
215 int n
= kcontrol
->private_value
;
217 uinfo
->type
= SNDRV_CTL_ELEM_TYPE_INTEGER
;
218 uinfo
->count
= (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
) ? 2 : 1;
219 uinfo
->value
.integer
.min
= wm
->ctl
[n
].min
;
220 uinfo
->value
.integer
.max
= wm
->ctl
[n
].max
;
225 static int snd_wm8766_enum_info(struct snd_kcontrol
*kcontrol
,
226 struct snd_ctl_elem_info
*uinfo
)
228 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
229 int n
= kcontrol
->private_value
;
231 return snd_ctl_enum_info(uinfo
, 1, wm
->ctl
[n
].max
,
232 wm
->ctl
[n
].enum_names
);
235 static int snd_wm8766_ctl_get(struct snd_kcontrol
*kcontrol
,
236 struct snd_ctl_elem_value
*ucontrol
)
238 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
239 int n
= kcontrol
->private_value
;
243 wm
->ctl
[n
].get(wm
, &val1
, &val2
);
245 val1
= wm
->regs
[wm
->ctl
[n
].reg1
] & wm
->ctl
[n
].mask1
;
246 val1
>>= __ffs(wm
->ctl
[n
].mask1
);
247 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
) {
248 val2
= wm
->regs
[wm
->ctl
[n
].reg2
] & wm
->ctl
[n
].mask2
;
249 val2
>>= __ffs(wm
->ctl
[n
].mask2
);
250 if (wm
->ctl
[n
].flags
& WM8766_FLAG_VOL_UPDATE
)
251 val2
&= ~WM8766_VOL_UPDATE
;
254 if (wm
->ctl
[n
].flags
& WM8766_FLAG_INVERT
) {
255 val1
= wm
->ctl
[n
].max
- (val1
- wm
->ctl
[n
].min
);
256 val2
= wm
->ctl
[n
].max
- (val2
- wm
->ctl
[n
].min
);
258 ucontrol
->value
.integer
.value
[0] = val1
;
259 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
)
260 ucontrol
->value
.integer
.value
[1] = val2
;
265 static int snd_wm8766_ctl_put(struct snd_kcontrol
*kcontrol
,
266 struct snd_ctl_elem_value
*ucontrol
)
268 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
269 int n
= kcontrol
->private_value
;
270 u16 val
, regval1
, regval2
;
272 /* this also works for enum because value is an union */
273 regval1
= ucontrol
->value
.integer
.value
[0];
274 regval2
= ucontrol
->value
.integer
.value
[1];
275 if (wm
->ctl
[n
].flags
& WM8766_FLAG_INVERT
) {
276 regval1
= wm
->ctl
[n
].max
- (regval1
- wm
->ctl
[n
].min
);
277 regval2
= wm
->ctl
[n
].max
- (regval2
- wm
->ctl
[n
].min
);
280 wm
->ctl
[n
].set(wm
, regval1
, regval2
);
282 val
= wm
->regs
[wm
->ctl
[n
].reg1
] & ~wm
->ctl
[n
].mask1
;
283 val
|= regval1
<< __ffs(wm
->ctl
[n
].mask1
);
284 /* both stereo controls in one register */
285 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
&&
286 wm
->ctl
[n
].reg1
== wm
->ctl
[n
].reg2
) {
287 val
&= ~wm
->ctl
[n
].mask2
;
288 val
|= regval2
<< __ffs(wm
->ctl
[n
].mask2
);
290 snd_wm8766_write(wm
, wm
->ctl
[n
].reg1
, val
);
291 /* stereo controls in different registers */
292 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
&&
293 wm
->ctl
[n
].reg1
!= wm
->ctl
[n
].reg2
) {
294 val
= wm
->regs
[wm
->ctl
[n
].reg2
] & ~wm
->ctl
[n
].mask2
;
295 val
|= regval2
<< __ffs(wm
->ctl
[n
].mask2
);
296 if (wm
->ctl
[n
].flags
& WM8766_FLAG_VOL_UPDATE
)
297 val
|= WM8766_VOL_UPDATE
;
298 snd_wm8766_write(wm
, wm
->ctl
[n
].reg2
, val
);
305 static int snd_wm8766_add_control(struct snd_wm8766
*wm
, int num
)
307 struct snd_kcontrol_new cont
;
308 struct snd_kcontrol
*ctl
;
310 memset(&cont
, 0, sizeof(cont
));
311 cont
.iface
= SNDRV_CTL_ELEM_IFACE_MIXER
;
312 cont
.private_value
= num
;
313 cont
.name
= wm
->ctl
[num
].name
;
314 cont
.access
= SNDRV_CTL_ELEM_ACCESS_READWRITE
;
315 if (wm
->ctl
[num
].flags
& WM8766_FLAG_LIM
||
316 wm
->ctl
[num
].flags
& WM8766_FLAG_ALC
)
317 cont
.access
|= SNDRV_CTL_ELEM_ACCESS_INACTIVE
;
319 cont
.get
= snd_wm8766_ctl_get
;
320 cont
.put
= snd_wm8766_ctl_put
;
322 switch (wm
->ctl
[num
].type
) {
323 case SNDRV_CTL_ELEM_TYPE_INTEGER
:
324 cont
.info
= snd_wm8766_volume_info
;
325 cont
.access
|= SNDRV_CTL_ELEM_ACCESS_TLV_READ
;
326 cont
.tlv
.p
= wm
->ctl
[num
].tlv
;
328 case SNDRV_CTL_ELEM_TYPE_BOOLEAN
:
329 wm
->ctl
[num
].max
= 1;
330 if (wm
->ctl
[num
].flags
& WM8766_FLAG_STEREO
)
331 cont
.info
= snd_ctl_boolean_stereo_info
;
333 cont
.info
= snd_ctl_boolean_mono_info
;
335 case SNDRV_CTL_ELEM_TYPE_ENUMERATED
:
336 cont
.info
= snd_wm8766_enum_info
;
341 ctl
= snd_ctl_new1(&cont
, wm
);
344 wm
->ctl
[num
].kctl
= ctl
;
346 return snd_ctl_add(wm
->card
, ctl
);
349 int snd_wm8766_build_controls(struct snd_wm8766
*wm
)
353 for (i
= 0; i
< WM8766_CTL_COUNT
; i
++)
354 if (wm
->ctl
[i
].name
) {
355 err
= snd_wm8766_add_control(wm
, i
);