Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/pci/ice1712/wm8766.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* ALSA driver for ICEnsemble VT17xx
4
*
5
* Lowlevel functions for WM8766 codec
6
*
7
* Copyright (c) 2012 Ondrej Zary <[email protected]>
8
*/
9
10
#include <linux/delay.h>
11
#include <sound/core.h>
12
#include <sound/control.h>
13
#include <sound/tlv.h>
14
#include "wm8766.h"
15
16
/* low-level access */
17
18
static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
19
{
20
if (addr < WM8766_REG_COUNT)
21
wm->regs[addr] = data;
22
wm->ops.write(wm, addr, data);
23
}
24
25
/* mixer controls */
26
27
static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
28
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,
33
.tlv = wm8766_tlv,
34
.reg1 = WM8766_REG_DACL1,
35
.reg2 = WM8766_REG_DACR1,
36
.mask1 = WM8766_VOL_MASK,
37
.mask2 = WM8766_VOL_MASK,
38
.max = 0xff,
39
.flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
40
},
41
[WM8766_CTL_CH2_VOL] = {
42
.name = "Channel 2 Playback Volume",
43
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
44
.tlv = wm8766_tlv,
45
.reg1 = WM8766_REG_DACL2,
46
.reg2 = WM8766_REG_DACR2,
47
.mask1 = WM8766_VOL_MASK,
48
.mask2 = WM8766_VOL_MASK,
49
.max = 0xff,
50
.flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
51
},
52
[WM8766_CTL_CH3_VOL] = {
53
.name = "Channel 3 Playback Volume",
54
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
55
.tlv = wm8766_tlv,
56
.reg1 = WM8766_REG_DACL3,
57
.reg2 = WM8766_REG_DACR3,
58
.mask1 = WM8766_VOL_MASK,
59
.mask2 = WM8766_VOL_MASK,
60
.max = 0xff,
61
.flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
62
},
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,
69
},
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,
76
},
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,
83
},
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,
89
},
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,
95
},
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,
101
},
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,
107
},
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,
113
},
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,
119
},
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,
125
},
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,
132
},
133
};
134
135
/* exported functions */
136
137
void snd_wm8766_init(struct snd_wm8766 *wm)
138
{
139
int i;
140
static const u16 default_values[] = {
141
0x000, 0x100,
142
0x120, 0x000,
143
0x000, 0x100, 0x000, 0x100, 0x000,
144
0x000, 0x080,
145
};
146
147
memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl));
148
149
snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */
150
udelay(10);
151
/* load defaults */
152
for (i = 0; i < ARRAY_SIZE(default_values); i++)
153
snd_wm8766_write(wm, i, default_values[i]);
154
}
155
156
void snd_wm8766_resume(struct snd_wm8766 *wm)
157
{
158
int i;
159
160
for (i = 0; i < WM8766_REG_COUNT; i++)
161
snd_wm8766_write(wm, i, wm->regs[i]);
162
}
163
164
void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac)
165
{
166
u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK;
167
168
dac &= WM8766_IF_MASK;
169
snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac);
170
}
171
172
void snd_wm8766_volume_restore(struct snd_wm8766 *wm)
173
{
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);
177
}
178
179
/* mixer callbacks */
180
181
static int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol,
182
struct snd_ctl_elem_info *uinfo)
183
{
184
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
185
int n = kcontrol->private_value;
186
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;
191
192
return 0;
193
}
194
195
static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol,
196
struct snd_ctl_elem_info *uinfo)
197
{
198
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
199
int n = kcontrol->private_value;
200
201
return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
202
wm->ctl[n].enum_names);
203
}
204
205
static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
206
struct snd_ctl_elem_value *ucontrol)
207
{
208
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
209
int n = kcontrol->private_value;
210
u16 val1, val2;
211
212
if (wm->ctl[n].get)
213
wm->ctl[n].get(wm, &val1, &val2);
214
else {
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;
222
}
223
}
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);
228
}
229
ucontrol->value.integer.value[0] = val1;
230
if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
231
ucontrol->value.integer.value[1] = val2;
232
233
return 0;
234
}
235
236
static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol,
237
struct snd_ctl_elem_value *ucontrol)
238
{
239
struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
240
int n = kcontrol->private_value;
241
u16 val, regval1, regval2;
242
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);
249
}
250
if (wm->ctl[n].set)
251
wm->ctl[n].set(wm, regval1, regval2);
252
else {
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);
260
}
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);
270
}
271
}
272
273
return 0;
274
}
275
276
static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num)
277
{
278
struct snd_kcontrol_new cont;
279
struct snd_kcontrol *ctl;
280
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;
289
cont.tlv.p = NULL;
290
cont.get = snd_wm8766_ctl_get;
291
cont.put = snd_wm8766_ctl_put;
292
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;
298
break;
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;
303
else
304
cont.info = snd_ctl_boolean_mono_info;
305
break;
306
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
307
cont.info = snd_wm8766_enum_info;
308
break;
309
default:
310
return -EINVAL;
311
}
312
ctl = snd_ctl_new1(&cont, wm);
313
if (!ctl)
314
return -ENOMEM;
315
wm->ctl[num].kctl = ctl;
316
317
return snd_ctl_add(wm->card, ctl);
318
}
319
320
int snd_wm8766_build_controls(struct snd_wm8766 *wm)
321
{
322
int err, i;
323
324
for (i = 0; i < WM8766_CTL_COUNT; i++)
325
if (wm->ctl[i].name) {
326
err = snd_wm8766_add_control(wm, i);
327
if (err < 0)
328
return err;
329
}
330
331
return 0;
332
}
333
334