1/*************************************************************************** 2 msnd_pinnacle_mixer.c - description 3 ------------------- 4 begin : Fre Jun 7 2002 5 copyright : (C) 2002 by karsten wiese 6 email : annabellesgarden@yahoo.de 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 55static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol, 56 struct snd_ctl_elem_info *uinfo) 57{ 58 static const char * const 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 64 return snd_ctl_enum_info(uinfo, 1, items, texts); 65} 66 67static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol, 68 struct snd_ctl_elem_value *ucontrol) 69{ 70 struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); 71 /* MSND_MASK_IMIX is the default */ 72 ucontrol->value.enumerated.item[0] = 0; 73 74 if (chip->recsrc & MSND_MASK_SYNTH) { 75 ucontrol->value.enumerated.item[0] = 1; 76 } else if ((chip->recsrc & MSND_MASK_DIGITAL) && 77 test_bit(F_HAVEDIGITAL, &chip->flags)) { 78 ucontrol->value.enumerated.item[0] = 2; 79 } 80 81 82 return 0; 83} 84 85static int snd_msndmix_set_mux(struct snd_msnd *chip, int val) 86{ 87 unsigned newrecsrc; 88 int change; 89 unsigned char msndbyte; 90 91 switch (val) { 92 case 0: 93 newrecsrc = MSND_MASK_IMIX; 94 msndbyte = HDEXAR_SET_ANA_IN; 95 break; 96 case 1: 97 newrecsrc = MSND_MASK_SYNTH; 98 msndbyte = HDEXAR_SET_SYNTH_IN; 99 break; 100 case 2: 101 newrecsrc = MSND_MASK_DIGITAL; 102 msndbyte = HDEXAR_SET_DAT_IN; 103 break; 104 default: 105 return -EINVAL; 106 } 107 change = newrecsrc != chip->recsrc; 108 if (change) { 109 change = 0; 110 if (!snd_msnd_send_word(chip, 0, 0, msndbyte)) 111 if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) { 112 chip->recsrc = newrecsrc; 113 change = 1; 114 } 115 } 116 return change; 117} 118 119static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol, 120 struct snd_ctl_elem_value *ucontrol) 121{ 122 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 123 return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]); 124} 125 126 127static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol, 128 struct snd_ctl_elem_info *uinfo) 129{ 130 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 131 uinfo->count = 2; 132 uinfo->value.integer.min = 0; 133 uinfo->value.integer.max = 100; 134 return 0; 135} 136 137static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol, 138 struct snd_ctl_elem_value *ucontrol) 139{ 140 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 141 int addr = kcontrol->private_value; 142 unsigned long flags; 143 144 spin_lock_irqsave(&msnd->mixer_lock, flags); 145 ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100; 146 ucontrol->value.integer.value[0] /= 0xFFFF; 147 ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100; 148 ucontrol->value.integer.value[1] /= 0xFFFF; 149 spin_unlock_irqrestore(&msnd->mixer_lock, flags); 150 return 0; 151} 152 153#define update_volm(a, b) \ 154 do { \ 155 writew((dev->left_levels[a] >> 1) * \ 156 readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ 157 dev->SMA + SMA_##b##Left); \ 158 writew((dev->right_levels[a] >> 1) * \ 159 readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ 160 dev->SMA + SMA_##b##Right); \ 161 } while (0); 162 163#define update_potm(d, s, ar) \ 164 do { \ 165 writeb((dev->left_levels[d] >> 8) * \ 166 readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ 167 dev->SMA + SMA_##s##Left); \ 168 writeb((dev->right_levels[d] >> 8) * \ 169 readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ 170 dev->SMA + SMA_##s##Right); \ 171 if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ 172 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ 173 } while (0); 174 175#define update_pot(d, s, ar) \ 176 do { \ 177 writeb(dev->left_levels[d] >> 8, \ 178 dev->SMA + SMA_##s##Left); \ 179 writeb(dev->right_levels[d] >> 8, \ 180 dev->SMA + SMA_##s##Right); \ 181 if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ 182 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ 183 } while (0); 184 185 186static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right) 187{ 188 int bLeft, bRight; 189 int wLeft, wRight; 190 int updatemaster = 0; 191 192 if (d >= LEVEL_ENTRIES) 193 return -EINVAL; 194 195 bLeft = left * 0xff / 100; 196 wLeft = left * 0xffff / 100; 197 198 bRight = right * 0xff / 100; 199 wRight = right * 0xffff / 100; 200 201 dev->left_levels[d] = wLeft; 202 dev->right_levels[d] = wRight; 203 204 switch (d) { 205 /* master volume unscaled controls */ 206 case MSND_MIXER_LINE: /* line pot control */ 207 /* scaled by IMIX in digital mix */ 208 writeb(bLeft, dev->SMA + SMA_bInPotPosLeft); 209 writeb(bRight, dev->SMA + SMA_bInPotPosRight); 210 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) 211 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 212 break; 213 case MSND_MIXER_MIC: /* mic pot control */ 214 if (dev->type == msndClassic) 215 return -EINVAL; 216 /* scaled by IMIX in digital mix */ 217 writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft); 218 writeb(bRight, dev->SMA + SMA_bMicPotPosRight); 219 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) 220 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 221 break; 222 case MSND_MIXER_VOLUME: /* master volume */ 223 writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft); 224 writew(wRight, dev->SMA + SMA_wCurrMastVolRight); 225 /* fall through */ 226 227 case MSND_MIXER_AUX: /* aux pot control */ 228 /* scaled by master volume */ 229 /* fall through */ 230 231 /* digital controls */ 232 case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */ 233 case MSND_MIXER_PCM: /* pcm vol (dsp mix) */ 234 case MSND_MIXER_IMIX: /* input monitor (dsp mix) */ 235 /* scaled by master volume */ 236 updatemaster = 1; 237 break; 238 239 default: 240 return -EINVAL; 241 } 242 243 if (updatemaster) { 244 /* update master volume scaled controls */ 245 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 246 update_volm(MSND_MIXER_IMIX, wCurrInVol); 247 if (dev->type == msndPinnacle) 248 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 249 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 250 } 251 252 return 0; 253} 254 255static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol, 256 struct snd_ctl_elem_value *ucontrol) 257{ 258 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 259 int change, addr = kcontrol->private_value; 260 int left, right; 261 unsigned long flags; 262 263 left = ucontrol->value.integer.value[0] % 101; 264 right = ucontrol->value.integer.value[1] % 101; 265 spin_lock_irqsave(&msnd->mixer_lock, flags); 266 change = msnd->left_levels[addr] != left 267 || msnd->right_levels[addr] != right; 268 snd_msndmix_set(msnd, addr, left, right); 269 spin_unlock_irqrestore(&msnd->mixer_lock, flags); 270 return change; 271} 272 273 274#define DUMMY_VOLUME(xname, xindex, addr) \ 275{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 276 .info = snd_msndmix_volume_info, \ 277 .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \ 278 .private_value = addr } 279 280 281static struct snd_kcontrol_new snd_msnd_controls[] = { 282DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME), 283DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM), 284DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX), 285DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE), 286DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC), 287DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX), 288{ 289 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 290 .name = "Capture Source", 291 .info = snd_msndmix_info_mux, 292 .get = snd_msndmix_get_mux, 293 .put = snd_msndmix_put_mux, 294} 295}; 296 297 298int snd_msndmix_new(struct snd_card *card) 299{ 300 struct snd_msnd *chip = card->private_data; 301 unsigned int idx; 302 int err; 303 304 if (snd_BUG_ON(!chip)) 305 return -EINVAL; 306 spin_lock_init(&chip->mixer_lock); 307 strcpy(card->mixername, "MSND Pinnacle Mixer"); 308 309 for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) { 310 err = snd_ctl_add(card, 311 snd_ctl_new1(snd_msnd_controls + idx, chip)); 312 if (err < 0) 313 return err; 314 } 315 316 return 0; 317} 318EXPORT_SYMBOL(snd_msndmix_new); 319 320void snd_msndmix_setup(struct snd_msnd *dev) 321{ 322 update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); 323 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 324 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 325 update_volm(MSND_MIXER_IMIX, wCurrInVol); 326 if (dev->type == msndPinnacle) { 327 update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); 328 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 329 } 330} 331EXPORT_SYMBOL(snd_msndmix_setup); 332 333int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc) 334{ 335 dev->recsrc = -1; 336 return snd_msndmix_set_mux(dev, recsrc); 337} 338EXPORT_SYMBOL(snd_msndmix_force_recsrc); 339