Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/isa/msnd/msnd_pinnacle_mixer.c
10817 views
1
/***************************************************************************
2
msnd_pinnacle_mixer.c - description
3
-------------------
4
begin : Fre Jun 7 2002
5
copyright : (C) 2002 by karsten wiese
6
email : [email protected]
7
***************************************************************************/
8
9
/***************************************************************************
10
* *
11
* This program is free software; you can redistribute it and/or modify *
12
* it under the terms of the GNU General Public License as published by *
13
* the Free Software Foundation; either version 2 of the License, or *
14
* (at your option) any later version. *
15
* *
16
***************************************************************************/
17
18
#include <linux/io.h>
19
20
#include <sound/core.h>
21
#include <sound/control.h>
22
#include "msnd.h"
23
#include "msnd_pinnacle.h"
24
25
26
#define MSND_MIXER_VOLUME 0
27
#define MSND_MIXER_PCM 1
28
#define MSND_MIXER_AUX 2 /* Input source 1 (aux1) */
29
#define MSND_MIXER_IMIX 3 /* Recording monitor */
30
#define MSND_MIXER_SYNTH 4
31
#define MSND_MIXER_SPEAKER 5
32
#define MSND_MIXER_LINE 6
33
#define MSND_MIXER_MIC 7
34
#define MSND_MIXER_RECLEV 11 /* Recording level */
35
#define MSND_MIXER_IGAIN 12 /* Input gain */
36
#define MSND_MIXER_OGAIN 13 /* Output gain */
37
#define MSND_MIXER_DIGITAL 17 /* Digital (input) 1 */
38
39
/* Device mask bits */
40
41
#define MSND_MASK_VOLUME (1 << MSND_MIXER_VOLUME)
42
#define MSND_MASK_SYNTH (1 << MSND_MIXER_SYNTH)
43
#define MSND_MASK_PCM (1 << MSND_MIXER_PCM)
44
#define MSND_MASK_SPEAKER (1 << MSND_MIXER_SPEAKER)
45
#define MSND_MASK_LINE (1 << MSND_MIXER_LINE)
46
#define MSND_MASK_MIC (1 << MSND_MIXER_MIC)
47
#define MSND_MASK_IMIX (1 << MSND_MIXER_IMIX)
48
#define MSND_MASK_RECLEV (1 << MSND_MIXER_RECLEV)
49
#define MSND_MASK_IGAIN (1 << MSND_MIXER_IGAIN)
50
#define MSND_MASK_OGAIN (1 << MSND_MIXER_OGAIN)
51
#define MSND_MASK_AUX (1 << MSND_MIXER_AUX)
52
#define MSND_MASK_DIGITAL (1 << MSND_MIXER_DIGITAL)
53
54
static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
55
struct snd_ctl_elem_info *uinfo)
56
{
57
static char *texts[3] = {
58
"Analog", "MASS", "SPDIF",
59
};
60
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
61
unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
62
63
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
64
uinfo->count = 1;
65
uinfo->value.enumerated.items = items;
66
if (uinfo->value.enumerated.item >= items)
67
uinfo->value.enumerated.item = items - 1;
68
strcpy(uinfo->value.enumerated.name,
69
texts[uinfo->value.enumerated.item]);
70
return 0;
71
}
72
73
static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
74
struct snd_ctl_elem_value *ucontrol)
75
{
76
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
77
/* MSND_MASK_IMIX is the default */
78
ucontrol->value.enumerated.item[0] = 0;
79
80
if (chip->recsrc & MSND_MASK_SYNTH) {
81
ucontrol->value.enumerated.item[0] = 1;
82
} else if ((chip->recsrc & MSND_MASK_DIGITAL) &&
83
test_bit(F_HAVEDIGITAL, &chip->flags)) {
84
ucontrol->value.enumerated.item[0] = 2;
85
}
86
87
88
return 0;
89
}
90
91
static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)
92
{
93
unsigned newrecsrc;
94
int change;
95
unsigned char msndbyte;
96
97
switch (val) {
98
case 0:
99
newrecsrc = MSND_MASK_IMIX;
100
msndbyte = HDEXAR_SET_ANA_IN;
101
break;
102
case 1:
103
newrecsrc = MSND_MASK_SYNTH;
104
msndbyte = HDEXAR_SET_SYNTH_IN;
105
break;
106
case 2:
107
newrecsrc = MSND_MASK_DIGITAL;
108
msndbyte = HDEXAR_SET_DAT_IN;
109
break;
110
default:
111
return -EINVAL;
112
}
113
change = newrecsrc != chip->recsrc;
114
if (change) {
115
change = 0;
116
if (!snd_msnd_send_word(chip, 0, 0, msndbyte))
117
if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {
118
chip->recsrc = newrecsrc;
119
change = 1;
120
}
121
}
122
return change;
123
}
124
125
static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,
126
struct snd_ctl_elem_value *ucontrol)
127
{
128
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
129
return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);
130
}
131
132
133
static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol,
134
struct snd_ctl_elem_info *uinfo)
135
{
136
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
137
uinfo->count = 2;
138
uinfo->value.integer.min = 0;
139
uinfo->value.integer.max = 100;
140
return 0;
141
}
142
143
static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
144
struct snd_ctl_elem_value *ucontrol)
145
{
146
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
147
int addr = kcontrol->private_value;
148
unsigned long flags;
149
150
spin_lock_irqsave(&msnd->mixer_lock, flags);
151
ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
152
ucontrol->value.integer.value[0] /= 0xFFFF;
153
ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
154
ucontrol->value.integer.value[1] /= 0xFFFF;
155
spin_unlock_irqrestore(&msnd->mixer_lock, flags);
156
return 0;
157
}
158
159
#define update_volm(a, b) \
160
do { \
161
writew((dev->left_levels[a] >> 1) * \
162
readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
163
dev->SMA + SMA_##b##Left); \
164
writew((dev->right_levels[a] >> 1) * \
165
readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
166
dev->SMA + SMA_##b##Right); \
167
} while (0);
168
169
#define update_potm(d, s, ar) \
170
do { \
171
writeb((dev->left_levels[d] >> 8) * \
172
readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
173
dev->SMA + SMA_##s##Left); \
174
writeb((dev->right_levels[d] >> 8) * \
175
readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
176
dev->SMA + SMA_##s##Right); \
177
if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \
178
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \
179
} while (0);
180
181
#define update_pot(d, s, ar) \
182
do { \
183
writeb(dev->left_levels[d] >> 8, \
184
dev->SMA + SMA_##s##Left); \
185
writeb(dev->right_levels[d] >> 8, \
186
dev->SMA + SMA_##s##Right); \
187
if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \
188
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \
189
} while (0);
190
191
192
static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
193
{
194
int bLeft, bRight;
195
int wLeft, wRight;
196
int updatemaster = 0;
197
198
if (d >= LEVEL_ENTRIES)
199
return -EINVAL;
200
201
bLeft = left * 0xff / 100;
202
wLeft = left * 0xffff / 100;
203
204
bRight = right * 0xff / 100;
205
wRight = right * 0xffff / 100;
206
207
dev->left_levels[d] = wLeft;
208
dev->right_levels[d] = wRight;
209
210
switch (d) {
211
/* master volume unscaled controls */
212
case MSND_MIXER_LINE: /* line pot control */
213
/* scaled by IMIX in digital mix */
214
writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);
215
writeb(bRight, dev->SMA + SMA_bInPotPosRight);
216
if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
217
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
218
break;
219
case MSND_MIXER_MIC: /* mic pot control */
220
if (dev->type == msndClassic)
221
return -EINVAL;
222
/* scaled by IMIX in digital mix */
223
writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);
224
writeb(bRight, dev->SMA + SMA_bMicPotPosRight);
225
if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
226
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
227
break;
228
case MSND_MIXER_VOLUME: /* master volume */
229
writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
230
writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
231
/* fall through */
232
233
case MSND_MIXER_AUX: /* aux pot control */
234
/* scaled by master volume */
235
/* fall through */
236
237
/* digital controls */
238
case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */
239
case MSND_MIXER_PCM: /* pcm vol (dsp mix) */
240
case MSND_MIXER_IMIX: /* input monitor (dsp mix) */
241
/* scaled by master volume */
242
updatemaster = 1;
243
break;
244
245
default:
246
return -EINVAL;
247
}
248
249
if (updatemaster) {
250
/* update master volume scaled controls */
251
update_volm(MSND_MIXER_PCM, wCurrPlayVol);
252
update_volm(MSND_MIXER_IMIX, wCurrInVol);
253
if (dev->type == msndPinnacle)
254
update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
255
update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
256
}
257
258
return 0;
259
}
260
261
static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
262
struct snd_ctl_elem_value *ucontrol)
263
{
264
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
265
int change, addr = kcontrol->private_value;
266
int left, right;
267
unsigned long flags;
268
269
left = ucontrol->value.integer.value[0] % 101;
270
right = ucontrol->value.integer.value[1] % 101;
271
spin_lock_irqsave(&msnd->mixer_lock, flags);
272
change = msnd->left_levels[addr] != left
273
|| msnd->right_levels[addr] != right;
274
snd_msndmix_set(msnd, addr, left, right);
275
spin_unlock_irqrestore(&msnd->mixer_lock, flags);
276
return change;
277
}
278
279
280
#define DUMMY_VOLUME(xname, xindex, addr) \
281
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
282
.info = snd_msndmix_volume_info, \
283
.get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
284
.private_value = addr }
285
286
287
static struct snd_kcontrol_new snd_msnd_controls[] = {
288
DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
289
DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
290
DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
291
DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
292
DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
293
DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
294
{
295
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
296
.name = "Capture Source",
297
.info = snd_msndmix_info_mux,
298
.get = snd_msndmix_get_mux,
299
.put = snd_msndmix_put_mux,
300
}
301
};
302
303
304
int __devinit snd_msndmix_new(struct snd_card *card)
305
{
306
struct snd_msnd *chip = card->private_data;
307
unsigned int idx;
308
int err;
309
310
if (snd_BUG_ON(!chip))
311
return -EINVAL;
312
spin_lock_init(&chip->mixer_lock);
313
strcpy(card->mixername, "MSND Pinnacle Mixer");
314
315
for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)
316
err = snd_ctl_add(card,
317
snd_ctl_new1(snd_msnd_controls + idx, chip));
318
if (err < 0)
319
return err;
320
321
return 0;
322
}
323
EXPORT_SYMBOL(snd_msndmix_new);
324
325
void snd_msndmix_setup(struct snd_msnd *dev)
326
{
327
update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
328
update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
329
update_volm(MSND_MIXER_PCM, wCurrPlayVol);
330
update_volm(MSND_MIXER_IMIX, wCurrInVol);
331
if (dev->type == msndPinnacle) {
332
update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
333
update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
334
}
335
}
336
EXPORT_SYMBOL(snd_msndmix_setup);
337
338
int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
339
{
340
dev->recsrc = -1;
341
return snd_msndmix_set_mux(dev, recsrc);
342
}
343
EXPORT_SYMBOL(snd_msndmix_force_recsrc);
344
345