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