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 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 64 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 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 { 137 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 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) 218 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 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) 227 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 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); 256 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 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 { 296 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 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 305 int snd_msndmix_new(struct snd_card *card) 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 } 324 EXPORT_SYMBOL(snd_msndmix_new); 325 326 void snd_msndmix_setup(struct snd_msnd *dev) 327 { 328 update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); 329 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 330 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 331 update_volm(MSND_MIXER_IMIX, wCurrInVol); 332 if (dev->type == msndPinnacle) { 333 update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); 334 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 335 } 336 } 337 EXPORT_SYMBOL(snd_msndmix_setup); 338 339 int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc) 340 { 341 dev->recsrc = -1; 342 return snd_msndmix_set_mux(dev, recsrc); 343 } 344 EXPORT_SYMBOL(snd_msndmix_force_recsrc); 345