1/* 2 ********************************************************************** 3 * efxmgr.c 4 * Copyright 1999, 2000 Creative Labs, Inc. 5 * 6 ********************************************************************** 7 * 8 * Date Author Summary of changes 9 * ---- ------ ------------------ 10 * October 20, 1999 Bertrand Lee base code release 11 * 12 ********************************************************************** 13 * 14 * This program is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU General Public License as 16 * published by the Free Software Foundation; either version 2 of 17 * the License, or (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public 25 * License along with this program; if not, write to the Free 26 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 27 * USA. 28 * 29 ********************************************************************** 30 */ 31 32#include <linux/bitops.h> 33#include "hwaccess.h" 34#include "efxmgr.h" 35 36int emu10k1_find_control_gpr(struct patch_manager *mgr, const char *patch_name, const char *gpr_name) 37{ 38 struct dsp_patch *patch; 39 struct dsp_rpatch *rpatch; 40 char s[PATCH_NAME_SIZE + 4]; 41 unsigned long *gpr_used; 42 int i; 43 44 DPD(2, "emu10k1_find_control_gpr(): %s %s\n", patch_name, gpr_name); 45 46 rpatch = &mgr->rpatch; 47 if (!strcmp(rpatch->name, patch_name)) { 48 gpr_used = rpatch->gpr_used; 49 goto match; 50 } 51 52 for (i = 0; i < mgr->current_pages * PATCHES_PER_PAGE; i++) { 53 patch = PATCH(mgr, i); 54 sprintf(s,"%s", patch->name); 55 56 if (!strcmp(s, patch_name)) { 57 gpr_used = patch->gpr_used; 58 goto match; 59 } 60 } 61 62 return -1; 63 64 match: 65 for (i = 0; i < NUM_GPRS; i++) 66 if (mgr->gpr[i].type == GPR_TYPE_CONTROL && 67 test_bit(i, gpr_used) && 68 !strcmp(mgr->gpr[i].name, gpr_name)) 69 return i; 70 71 return -1; 72} 73 74void emu10k1_set_control_gpr(struct emu10k1_card *card, int addr, s32 val, int flag) 75{ 76 struct patch_manager *mgr = &card->mgr; 77 78 DPD(2, "emu10k1_set_control_gpr(): %d %x\n", addr, val); 79 80 if (addr < 0 || addr >= NUM_GPRS) 81 return; 82 83 if (flag) 84 val += sblive_readptr(card, GPR_BASE + addr, 0); 85 86 if (val > mgr->gpr[addr].max) 87 val = mgr->gpr[addr].max; 88 else if (val < mgr->gpr[addr].min) 89 val = mgr->gpr[addr].min; 90 91 sblive_writeptr(card, GPR_BASE + addr, 0, val); 92} 93 94//TODO: make this configurable: 95#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME 96#define VOLCTRL_STEP_SIZE 5 97 98//An internal function for setting OSS mixer controls. 99void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer, 100 unsigned int left, unsigned int right) 101{ 102 extern char volume_params[SOUND_MIXER_NRDEVICES]; 103 104 card->ac97.mixer_state[oss_mixer] = (right << 8) | left; 105 106 if (!card->is_aps) 107 card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); 108 109 emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, 110 volume_params[oss_mixer]); 111 112 emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, 113 volume_params[oss_mixer]); 114} 115 116//FIXME: mute should unmute when pressed a second time 117void emu10k1_mute_irqhandler(struct emu10k1_card *card) 118{ 119 int oss_channel = VOLCTRL_CHANNEL; 120 int left, right; 121 static int val = 0; 122 123 if (val) { 124 left = val & 0xff; 125 right = (val >> 8) & 0xff; 126 val = 0; 127 } else { 128 val = card->ac97.mixer_state[oss_channel]; 129 left = 0; 130 right = 0; 131 } 132 133 emu10k1_set_oss_vol(card, oss_channel, left, right); 134} 135 136void emu10k1_volincr_irqhandler(struct emu10k1_card *card) 137{ 138 int oss_channel = VOLCTRL_CHANNEL; 139 int left, right; 140 141 left = card->ac97.mixer_state[oss_channel] & 0xff; 142 right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; 143 144 if ((left += VOLCTRL_STEP_SIZE) > 100) 145 left = 100; 146 147 if ((right += VOLCTRL_STEP_SIZE) > 100) 148 right = 100; 149 150 emu10k1_set_oss_vol(card, oss_channel, left, right); 151} 152 153void emu10k1_voldecr_irqhandler(struct emu10k1_card *card) 154{ 155 int oss_channel = VOLCTRL_CHANNEL; 156 int left, right; 157 158 left = card->ac97.mixer_state[oss_channel] & 0xff; 159 right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; 160 161 if ((left -= VOLCTRL_STEP_SIZE) < 0) 162 left = 0; 163 164 if ((right -= VOLCTRL_STEP_SIZE) < 0) 165 right = 0; 166 167 emu10k1_set_oss_vol(card, oss_channel, left, right); 168} 169 170void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale) 171{ 172 struct patch_manager *mgr = &card->mgr; 173 unsigned long flags; 174 175 static const s32 log2lin[4] ={ // attenuation (dB) 176 0x7fffffff, // 0.0 177 0x7fffffff * 0.840896415253715 , // 1.5 178 0x7fffffff * 0.707106781186548, // 3.0 179 0x7fffffff * 0.594603557501361 , // 4.5 180 }; 181 182 if (addr < 0) 183 return; 184 185 vol = (100 - vol ) * scale / 100; 186 187 // Thanks to the comp.dsp newsgroup for this neat trick: 188 vol = (vol >= scale) ? 0 : (log2lin[vol & 3] >> (vol >> 2)); 189 190 spin_lock_irqsave(&mgr->lock, flags); 191 emu10k1_set_control_gpr(card, addr, vol, 0); 192 spin_unlock_irqrestore(&mgr->lock, flags); 193} 194 195void emu10k1_dsp_irqhandler(struct emu10k1_card *card) 196{ 197 unsigned long flags; 198 199 if (card->pt.state != PT_STATE_INACTIVE) { 200 u32 bc; 201 bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0); 202 if (bc != 0) { 203 DPD(3, "pt interrupt, bc = %d\n", bc); 204 spin_lock_irqsave(&card->pt.lock, flags); 205 card->pt.blocks_played = bc; 206 if (card->pt.blocks_played >= card->pt.blocks_copied) { 207 DPF(1, "buffer underrun in passthrough playback\n"); 208 emu10k1_pt_stop(card); 209 } 210 wake_up_interruptible(&card->pt.wait); 211 spin_unlock_irqrestore(&card->pt.lock, flags); 212 } 213 } 214} 215 216