ALSA: ice1712: Constify wm-specific tables
[muen/linux.git] / sound / pci / ice1712 / wm8766.c
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 <linux@rainbow-software.org>
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 }