1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ALSA driver for ICEnsemble VT17xx
5 * Lowlevel functions for WM8766 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_wm8766_write(struct snd_wm8766
*wm
, u16 addr
, u16 data
)
20 if (addr
< WM8766_REG_COUNT
)
21 wm
->regs
[addr
] = data
;
22 wm
->ops
.write(wm
, addr
, data
);
27 static const DECLARE_TLV_DB_SCALE(wm8766_tlv
, -12750, 50, 1);
29 static const struct snd_wm8766_ctl snd_wm8766_default_ctl
[WM8766_CTL_COUNT
] = {
30 [WM8766_CTL_CH1_VOL
] = {
31 .name
= "Channel 1 Playback Volume",
32 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
34 .reg1
= WM8766_REG_DACL1
,
35 .reg2
= WM8766_REG_DACR1
,
36 .mask1
= WM8766_VOL_MASK
,
37 .mask2
= WM8766_VOL_MASK
,
39 .flags
= WM8766_FLAG_STEREO
| WM8766_FLAG_VOL_UPDATE
,
41 [WM8766_CTL_CH2_VOL
] = {
42 .name
= "Channel 2 Playback Volume",
43 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
45 .reg1
= WM8766_REG_DACL2
,
46 .reg2
= WM8766_REG_DACR2
,
47 .mask1
= WM8766_VOL_MASK
,
48 .mask2
= WM8766_VOL_MASK
,
50 .flags
= WM8766_FLAG_STEREO
| WM8766_FLAG_VOL_UPDATE
,
52 [WM8766_CTL_CH3_VOL
] = {
53 .name
= "Channel 3 Playback Volume",
54 .type
= SNDRV_CTL_ELEM_TYPE_INTEGER
,
56 .reg1
= WM8766_REG_DACL3
,
57 .reg2
= WM8766_REG_DACR3
,
58 .mask1
= WM8766_VOL_MASK
,
59 .mask2
= WM8766_VOL_MASK
,
61 .flags
= WM8766_FLAG_STEREO
| WM8766_FLAG_VOL_UPDATE
,
63 [WM8766_CTL_CH1_SW
] = {
64 .name
= "Channel 1 Playback Switch",
65 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
66 .reg1
= WM8766_REG_DACCTRL2
,
67 .mask1
= WM8766_DAC2_MUTE1
,
68 .flags
= WM8766_FLAG_INVERT
,
70 [WM8766_CTL_CH2_SW
] = {
71 .name
= "Channel 2 Playback Switch",
72 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
73 .reg1
= WM8766_REG_DACCTRL2
,
74 .mask1
= WM8766_DAC2_MUTE2
,
75 .flags
= WM8766_FLAG_INVERT
,
77 [WM8766_CTL_CH3_SW
] = {
78 .name
= "Channel 3 Playback Switch",
79 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
80 .reg1
= WM8766_REG_DACCTRL2
,
81 .mask1
= WM8766_DAC2_MUTE3
,
82 .flags
= WM8766_FLAG_INVERT
,
84 [WM8766_CTL_PHASE1_SW
] = {
85 .name
= "Channel 1 Phase Invert Playback Switch",
86 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
87 .reg1
= WM8766_REG_IFCTRL
,
88 .mask1
= WM8766_PHASE_INVERT1
,
90 [WM8766_CTL_PHASE2_SW
] = {
91 .name
= "Channel 2 Phase Invert Playback Switch",
92 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
93 .reg1
= WM8766_REG_IFCTRL
,
94 .mask1
= WM8766_PHASE_INVERT2
,
96 [WM8766_CTL_PHASE3_SW
] = {
97 .name
= "Channel 3 Phase Invert Playback Switch",
98 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
99 .reg1
= WM8766_REG_IFCTRL
,
100 .mask1
= WM8766_PHASE_INVERT3
,
102 [WM8766_CTL_DEEMPH1_SW
] = {
103 .name
= "Channel 1 Deemphasis Playback Switch",
104 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
105 .reg1
= WM8766_REG_DACCTRL2
,
106 .mask1
= WM8766_DAC2_DEEMP1
,
108 [WM8766_CTL_DEEMPH2_SW
] = {
109 .name
= "Channel 2 Deemphasis Playback Switch",
110 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
111 .reg1
= WM8766_REG_DACCTRL2
,
112 .mask1
= WM8766_DAC2_DEEMP2
,
114 [WM8766_CTL_DEEMPH3_SW
] = {
115 .name
= "Channel 3 Deemphasis Playback Switch",
116 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
117 .reg1
= WM8766_REG_DACCTRL2
,
118 .mask1
= WM8766_DAC2_DEEMP3
,
120 [WM8766_CTL_IZD_SW
] = {
121 .name
= "Infinite Zero Detect Playback Switch",
122 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
123 .reg1
= WM8766_REG_DACCTRL1
,
124 .mask1
= WM8766_DAC_IZD
,
126 [WM8766_CTL_ZC_SW
] = {
127 .name
= "Zero Cross Detect Playback Switch",
128 .type
= SNDRV_CTL_ELEM_TYPE_BOOLEAN
,
129 .reg1
= WM8766_REG_DACCTRL2
,
130 .mask1
= WM8766_DAC2_ZCD
,
131 .flags
= WM8766_FLAG_INVERT
,
135 /* exported functions */
137 void snd_wm8766_init(struct snd_wm8766
*wm
)
140 static const u16 default_values
[] = {
143 0x000, 0x100, 0x000, 0x100, 0x000,
147 memcpy(wm
->ctl
, snd_wm8766_default_ctl
, sizeof(wm
->ctl
));
149 snd_wm8766_write(wm
, WM8766_REG_RESET
, 0x00); /* reset */
152 for (i
= 0; i
< ARRAY_SIZE(default_values
); i
++)
153 snd_wm8766_write(wm
, i
, default_values
[i
]);
156 void snd_wm8766_resume(struct snd_wm8766
*wm
)
160 for (i
= 0; i
< WM8766_REG_COUNT
; i
++)
161 snd_wm8766_write(wm
, i
, wm
->regs
[i
]);
164 void snd_wm8766_set_if(struct snd_wm8766
*wm
, u16 dac
)
166 u16 val
= wm
->regs
[WM8766_REG_IFCTRL
] & ~WM8766_IF_MASK
;
168 dac
&= WM8766_IF_MASK
;
169 snd_wm8766_write(wm
, WM8766_REG_IFCTRL
, val
| dac
);
172 void snd_wm8766_volume_restore(struct snd_wm8766
*wm
)
174 u16 val
= wm
->regs
[WM8766_REG_DACR1
];
175 /* restore volume after MCLK stopped */
176 snd_wm8766_write(wm
, WM8766_REG_DACR1
, val
| WM8766_VOL_UPDATE
);
179 /* mixer callbacks */
181 static int snd_wm8766_volume_info(struct snd_kcontrol
*kcontrol
,
182 struct snd_ctl_elem_info
*uinfo
)
184 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
185 int n
= kcontrol
->private_value
;
187 uinfo
->type
= SNDRV_CTL_ELEM_TYPE_INTEGER
;
188 uinfo
->count
= (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
) ? 2 : 1;
189 uinfo
->value
.integer
.min
= wm
->ctl
[n
].min
;
190 uinfo
->value
.integer
.max
= wm
->ctl
[n
].max
;
195 static int snd_wm8766_enum_info(struct snd_kcontrol
*kcontrol
,
196 struct snd_ctl_elem_info
*uinfo
)
198 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
199 int n
= kcontrol
->private_value
;
201 return snd_ctl_enum_info(uinfo
, 1, wm
->ctl
[n
].max
,
202 wm
->ctl
[n
].enum_names
);
205 static int snd_wm8766_ctl_get(struct snd_kcontrol
*kcontrol
,
206 struct snd_ctl_elem_value
*ucontrol
)
208 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
209 int n
= kcontrol
->private_value
;
213 wm
->ctl
[n
].get(wm
, &val1
, &val2
);
215 val1
= wm
->regs
[wm
->ctl
[n
].reg1
] & wm
->ctl
[n
].mask1
;
216 val1
>>= __ffs(wm
->ctl
[n
].mask1
);
217 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
) {
218 val2
= wm
->regs
[wm
->ctl
[n
].reg2
] & wm
->ctl
[n
].mask2
;
219 val2
>>= __ffs(wm
->ctl
[n
].mask2
);
220 if (wm
->ctl
[n
].flags
& WM8766_FLAG_VOL_UPDATE
)
221 val2
&= ~WM8766_VOL_UPDATE
;
224 if (wm
->ctl
[n
].flags
& WM8766_FLAG_INVERT
) {
225 val1
= wm
->ctl
[n
].max
- (val1
- wm
->ctl
[n
].min
);
226 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
)
227 val2
= wm
->ctl
[n
].max
- (val2
- wm
->ctl
[n
].min
);
229 ucontrol
->value
.integer
.value
[0] = val1
;
230 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
)
231 ucontrol
->value
.integer
.value
[1] = val2
;
236 static int snd_wm8766_ctl_put(struct snd_kcontrol
*kcontrol
,
237 struct snd_ctl_elem_value
*ucontrol
)
239 struct snd_wm8766
*wm
= snd_kcontrol_chip(kcontrol
);
240 int n
= kcontrol
->private_value
;
241 u16 val
, regval1
, regval2
;
243 /* this also works for enum because value is a union */
244 regval1
= ucontrol
->value
.integer
.value
[0];
245 regval2
= ucontrol
->value
.integer
.value
[1];
246 if (wm
->ctl
[n
].flags
& WM8766_FLAG_INVERT
) {
247 regval1
= wm
->ctl
[n
].max
- (regval1
- wm
->ctl
[n
].min
);
248 regval2
= wm
->ctl
[n
].max
- (regval2
- wm
->ctl
[n
].min
);
251 wm
->ctl
[n
].set(wm
, regval1
, regval2
);
253 val
= wm
->regs
[wm
->ctl
[n
].reg1
] & ~wm
->ctl
[n
].mask1
;
254 val
|= regval1
<< __ffs(wm
->ctl
[n
].mask1
);
255 /* both stereo controls in one register */
256 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
&&
257 wm
->ctl
[n
].reg1
== wm
->ctl
[n
].reg2
) {
258 val
&= ~wm
->ctl
[n
].mask2
;
259 val
|= regval2
<< __ffs(wm
->ctl
[n
].mask2
);
261 snd_wm8766_write(wm
, wm
->ctl
[n
].reg1
, val
);
262 /* stereo controls in different registers */
263 if (wm
->ctl
[n
].flags
& WM8766_FLAG_STEREO
&&
264 wm
->ctl
[n
].reg1
!= wm
->ctl
[n
].reg2
) {
265 val
= wm
->regs
[wm
->ctl
[n
].reg2
] & ~wm
->ctl
[n
].mask2
;
266 val
|= regval2
<< __ffs(wm
->ctl
[n
].mask2
);
267 if (wm
->ctl
[n
].flags
& WM8766_FLAG_VOL_UPDATE
)
268 val
|= WM8766_VOL_UPDATE
;
269 snd_wm8766_write(wm
, wm
->ctl
[n
].reg2
, val
);
276 static int snd_wm8766_add_control(struct snd_wm8766
*wm
, int num
)
278 struct snd_kcontrol_new cont
;
279 struct snd_kcontrol
*ctl
;
281 memset(&cont
, 0, sizeof(cont
));
282 cont
.iface
= SNDRV_CTL_ELEM_IFACE_MIXER
;
283 cont
.private_value
= num
;
284 cont
.name
= wm
->ctl
[num
].name
;
285 cont
.access
= SNDRV_CTL_ELEM_ACCESS_READWRITE
;
286 if (wm
->ctl
[num
].flags
& WM8766_FLAG_LIM
||
287 wm
->ctl
[num
].flags
& WM8766_FLAG_ALC
)
288 cont
.access
|= SNDRV_CTL_ELEM_ACCESS_INACTIVE
;
290 cont
.get
= snd_wm8766_ctl_get
;
291 cont
.put
= snd_wm8766_ctl_put
;
293 switch (wm
->ctl
[num
].type
) {
294 case SNDRV_CTL_ELEM_TYPE_INTEGER
:
295 cont
.info
= snd_wm8766_volume_info
;
296 cont
.access
|= SNDRV_CTL_ELEM_ACCESS_TLV_READ
;
297 cont
.tlv
.p
= wm
->ctl
[num
].tlv
;
299 case SNDRV_CTL_ELEM_TYPE_BOOLEAN
:
300 wm
->ctl
[num
].max
= 1;
301 if (wm
->ctl
[num
].flags
& WM8766_FLAG_STEREO
)
302 cont
.info
= snd_ctl_boolean_stereo_info
;
304 cont
.info
= snd_ctl_boolean_mono_info
;
306 case SNDRV_CTL_ELEM_TYPE_ENUMERATED
:
307 cont
.info
= snd_wm8766_enum_info
;
312 ctl
= snd_ctl_new1(&cont
, wm
);
315 wm
->ctl
[num
].kctl
= ctl
;
317 return snd_ctl_add(wm
->card
, ctl
);
320 int snd_wm8766_build_controls(struct snd_wm8766
*wm
)
324 for (i
= 0; i
< WM8766_CTL_COUNT
; i
++)
325 if (wm
->ctl
[i
].name
) {
326 err
= snd_wm8766_add_control(wm
, i
);