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 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