~hui.wang/alsa-driver/tiwai-trunk-fgit

« back to all changes in this revision

Viewing changes to pci/ice1712/wm8766.c

  • Committer: Hui Wang
  • Date: 2018-06-07 01:04:04 UTC
  • Revision ID: git-v1:baf13208df10376d9e4588ad3524aeb3c9973bdf
sync the alsa hda driver from Takashi's tree

Signed-off-by: Hui Wang <hui.wang@canonical.com>

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   ALSA driver for ICEnsemble VT17xx
 
3
 *
 
4
 *   Lowlevel functions for WM8766 codec
 
5
 *
 
6
 *      Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 *
 
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
 
21
 *
 
22
 */
 
23
 
 
24
#include <linux/delay.h>
 
25
#include <sound/core.h>
 
26
#include <sound/control.h>
 
27
#include <sound/tlv.h>
 
28
#include "wm8766.h"
 
29
 
 
30
/* low-level access */
 
31
 
 
32
static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
 
33
{
 
34
        if (addr < WM8766_REG_COUNT)
 
35
                wm->regs[addr] = data;
 
36
        wm->ops.write(wm, addr, data);
 
37
}
 
38
 
 
39
/* mixer controls */
 
40
 
 
41
static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
 
42
 
 
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,
 
47
                .tlv = wm8766_tlv,
 
48
                .reg1 = WM8766_REG_DACL1,
 
49
                .reg2 = WM8766_REG_DACR1,
 
50
                .mask1 = WM8766_VOL_MASK,
 
51
                .mask2 = WM8766_VOL_MASK,
 
52
                .max = 0xff,
 
53
                .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
 
54
        },
 
55
        [WM8766_CTL_CH2_VOL] = {
 
56
                .name = "Channel 2 Playback Volume",
 
57
                .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
 
58
                .tlv = wm8766_tlv,
 
59
                .reg1 = WM8766_REG_DACL2,
 
60
                .reg2 = WM8766_REG_DACR2,
 
61
                .mask1 = WM8766_VOL_MASK,
 
62
                .mask2 = WM8766_VOL_MASK,
 
63
                .max = 0xff,
 
64
                .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
 
65
        },
 
66
        [WM8766_CTL_CH3_VOL] = {
 
67
                .name = "Channel 3 Playback Volume",
 
68
                .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
 
69
                .tlv = wm8766_tlv,
 
70
                .reg1 = WM8766_REG_DACL3,
 
71
                .reg2 = WM8766_REG_DACR3,
 
72
                .mask1 = WM8766_VOL_MASK,
 
73
                .mask2 = WM8766_VOL_MASK,
 
74
                .max = 0xff,
 
75
                .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
 
76
        },
 
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,
 
83
        },
 
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,
 
90
        },
 
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,
 
97
        },
 
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,
 
103
        },
 
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,
 
109
        },
 
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,
 
115
        },
 
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,
 
121
        },
 
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,
 
127
        },
 
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,
 
133
        },
 
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,
 
139
        },
 
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,
 
146
        },
 
147
};
 
148
 
 
149
/* exported functions */
 
150
 
 
151
void snd_wm8766_init(struct snd_wm8766 *wm)
 
152
{
 
153
        int i;
 
154
        static const u16 default_values[] = {
 
155
                0x000, 0x100,
 
156
                0x120, 0x000,
 
157
                0x000, 0x100, 0x000, 0x100, 0x000,
 
158
                0x000, 0x080,
 
159
        };
 
160
 
 
161
        memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl));
 
162
 
 
163
        snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */
 
164
        udelay(10);
 
165
        /* load defaults */
 
166
        for (i = 0; i < ARRAY_SIZE(default_values); i++)
 
167
                snd_wm8766_write(wm, i, default_values[i]);
 
168
}
 
169
 
 
170
void snd_wm8766_resume(struct snd_wm8766 *wm)
 
171
{
 
172
        int i;
 
173
 
 
174
        for (i = 0; i < WM8766_REG_COUNT; i++)
 
175
                snd_wm8766_write(wm, i, wm->regs[i]);
 
176
}
 
177
 
 
178
void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac)
 
179
{
 
180
        u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK;
 
181
 
 
182
        dac &= WM8766_IF_MASK;
 
183
        snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac);
 
184
}
 
185
 
 
186
void snd_wm8766_volume_restore(struct snd_wm8766 *wm)
 
187
{
 
188
        u16 val = wm->regs[WM8766_REG_DACR1];
 
189
        /* restore volume after MCLK stopped */
 
190
        snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE);
 
191
}
 
192
 
 
193
/* mixer callbacks */
 
194
 
 
195
static int snd_wm8766_volume_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
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 
202
        uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1;
 
203
        uinfo->value.integer.min = wm->ctl[n].min;
 
204
        uinfo->value.integer.max = wm->ctl[n].max;
 
205
 
 
206
        return 0;
 
207
}
 
208
 
 
209
static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol,
 
210
                                      struct snd_ctl_elem_info *uinfo)
 
211
{
 
212
        struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
 
213
        int n = kcontrol->private_value;
 
214
 
 
215
        return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
 
216
                                                wm->ctl[n].enum_names);
 
217
}
 
218
 
 
219
static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
 
220
                                  struct snd_ctl_elem_value *ucontrol)
 
221
{
 
222
        struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
 
223
        int n = kcontrol->private_value;
 
224
        u16 val1, val2;
 
225
 
 
226
        if (wm->ctl[n].get)
 
227
                wm->ctl[n].get(wm, &val1, &val2);
 
228
        else {
 
229
                val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
 
230
                val1 >>= __ffs(wm->ctl[n].mask1);
 
231
                if (wm->ctl[n].flags & WM8766_FLAG_STEREO) {
 
232
                        val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
 
233
                        val2 >>= __ffs(wm->ctl[n].mask2);
 
234
                        if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
 
235
                                val2 &= ~WM8766_VOL_UPDATE;
 
236
                }
 
237
        }
 
238
        if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
 
239
                val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
 
240
                if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
 
241
                        val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
 
242
        }
 
243
        ucontrol->value.integer.value[0] = val1;
 
244
        if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
 
245
                ucontrol->value.integer.value[1] = val2;
 
246
 
 
247
        return 0;
 
248
}
 
249
 
 
250
static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol,
 
251
                                  struct snd_ctl_elem_value *ucontrol)
 
252
{
 
253
        struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
 
254
        int n = kcontrol->private_value;
 
255
        u16 val, regval1, regval2;
 
256
 
 
257
        /* this also works for enum because value is a union */
 
258
        regval1 = ucontrol->value.integer.value[0];
 
259
        regval2 = ucontrol->value.integer.value[1];
 
260
        if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
 
261
                regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
 
262
                regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
 
263
        }
 
264
        if (wm->ctl[n].set)
 
265
                wm->ctl[n].set(wm, regval1, regval2);
 
266
        else {
 
267
                val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
 
268
                val |= regval1 << __ffs(wm->ctl[n].mask1);
 
269
                /* both stereo controls in one register */
 
270
                if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
 
271
                                wm->ctl[n].reg1 == wm->ctl[n].reg2) {
 
272
                        val &= ~wm->ctl[n].mask2;
 
273
                        val |= regval2 << __ffs(wm->ctl[n].mask2);
 
274
                }
 
275
                snd_wm8766_write(wm, wm->ctl[n].reg1, val);
 
276
                /* stereo controls in different registers */
 
277
                if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
 
278
                                wm->ctl[n].reg1 != wm->ctl[n].reg2) {
 
279
                        val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
 
280
                        val |= regval2 << __ffs(wm->ctl[n].mask2);
 
281
                        if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
 
282
                                val |= WM8766_VOL_UPDATE;
 
283
                        snd_wm8766_write(wm, wm->ctl[n].reg2, val);
 
284
                }
 
285
        }
 
286
 
 
287
        return 0;
 
288
}
 
289
 
 
290
static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num)
 
291
{
 
292
        struct snd_kcontrol_new cont;
 
293
        struct snd_kcontrol *ctl;
 
294
 
 
295
        memset(&cont, 0, sizeof(cont));
 
296
        cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 
297
        cont.private_value = num;
 
298
        cont.name = wm->ctl[num].name;
 
299
        cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
 
300
        if (wm->ctl[num].flags & WM8766_FLAG_LIM ||
 
301
            wm->ctl[num].flags & WM8766_FLAG_ALC)
 
302
                cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 
303
        cont.tlv.p = NULL;
 
304
        cont.get = snd_wm8766_ctl_get;
 
305
        cont.put = snd_wm8766_ctl_put;
 
306
 
 
307
        switch (wm->ctl[num].type) {
 
308
        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 
309
                cont.info = snd_wm8766_volume_info;
 
310
                cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 
311
                cont.tlv.p = wm->ctl[num].tlv;
 
312
                break;
 
313
        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 
314
                wm->ctl[num].max = 1;
 
315
                if (wm->ctl[num].flags & WM8766_FLAG_STEREO)
 
316
                        cont.info = snd_ctl_boolean_stereo_info;
 
317
                else
 
318
                        cont.info = snd_ctl_boolean_mono_info;
 
319
                break;
 
320
        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 
321
                cont.info = snd_wm8766_enum_info;
 
322
                break;
 
323
        default:
 
324
                return -EINVAL;
 
325
        }
 
326
        ctl = snd_ctl_new1(&cont, wm);
 
327
        if (!ctl)
 
328
                return -ENOMEM;
 
329
        wm->ctl[num].kctl = ctl;
 
330
 
 
331
        return snd_ctl_add(wm->card, ctl);
 
332
}
 
333
 
 
334
int snd_wm8766_build_controls(struct snd_wm8766 *wm)
 
335
{
 
336
        int err, i;
 
337
 
 
338
        for (i = 0; i < WM8766_CTL_COUNT; i++)
 
339
                if (wm->ctl[i].name) {
 
340
                        err = snd_wm8766_add_control(wm, i);
 
341
                        if (err < 0)
 
342
                                return err;
 
343
                }
 
344
 
 
345
        return 0;
 
346
}