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 (card->is_audigy) { 84 sblive_writeptr(card, A_GPR_BASE + addr, 0, val); 85 } else { 86 if (flag) 87 val += sblive_readptr(card, GPR_BASE + addr, 0); 88 if (val > mgr->gpr[addr].max) 89 val = mgr->gpr[addr].max; 90 else if (val < mgr->gpr[addr].min) 91 val = mgr->gpr[addr].min; 92 sblive_writeptr(card, GPR_BASE + addr, 0, val); 93 } 94 95 96} 97 98//TODO: make this configurable: 99#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME 100#define VOLCTRL_STEP_SIZE 5 101 102//An internal function for setting OSS mixer controls. 103static void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer, 104 unsigned int left, unsigned int right) 105{ 106 extern char volume_params[SOUND_MIXER_NRDEVICES]; 107 108 card->ac97->mixer_state[oss_mixer] = (right << 8) | left; 109 110 if (!card->is_aps) 111 card->ac97->write_mixer(card->ac97, oss_mixer, left, right); 112 113 emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, 114 volume_params[oss_mixer]); 115 116 emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, 117 volume_params[oss_mixer]); 118} 119 120void emu10k1_mute_irqhandler(struct emu10k1_card *card) 121{ 122 int oss_channel = VOLCTRL_CHANNEL; 123 int left, right; 124 static int val; 125 126 if (val) { 127 left = val & 0xff; 128 right = (val >> 8) & 0xff; 129 val = 0; 130 } else { 131 val = card->ac97->mixer_state[oss_channel]; 132 left = 0; 133 right = 0; 134 } 135 136 emu10k1_set_oss_vol(card, oss_channel, left, right); 137} 138 139void emu10k1_volincr_irqhandler(struct emu10k1_card *card) 140{ 141 int oss_channel = VOLCTRL_CHANNEL; 142 int left, right; 143 144 left = card->ac97->mixer_state[oss_channel] & 0xff; 145 right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff; 146 147 if ((left += VOLCTRL_STEP_SIZE) > 100) 148 left = 100; 149 150 if ((right += VOLCTRL_STEP_SIZE) > 100) 151 right = 100; 152 153 emu10k1_set_oss_vol(card, oss_channel, left, right); 154} 155 156void emu10k1_voldecr_irqhandler(struct emu10k1_card *card) 157{ 158 int oss_channel = VOLCTRL_CHANNEL; 159 int left, right; 160 161 left = card->ac97->mixer_state[oss_channel] & 0xff; 162 right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff; 163 164 if ((left -= VOLCTRL_STEP_SIZE) < 0) 165 left = 0; 166 167 if ((right -= VOLCTRL_STEP_SIZE) < 0) 168 right = 0; 169 170 emu10k1_set_oss_vol(card, oss_channel, left, right); 171} 172 173void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale) 174{ 175 struct patch_manager *mgr = &card->mgr; 176 unsigned long flags; 177 178 static const s32 log2lin[4] ={ // attenuation (dB) 179 0x7fffffff, // 0.0 180 0x7fffffff * 0.840896415253715 , // 1.5 181 0x7fffffff * 0.707106781186548, // 3.0 182 0x7fffffff * 0.594603557501361 , // 4.5 183 }; 184 185 if (addr < 0) 186 return; 187 188 vol = (100 - vol ) * scale / 100; 189 190 // Thanks to the comp.dsp newsgroup for this neat trick: 191 vol = (vol >= scale) ? 0 : (log2lin[vol & 3] >> (vol >> 2)); 192 193 spin_lock_irqsave(&mgr->lock, flags); 194 emu10k1_set_control_gpr(card, addr, vol, 0); 195 spin_unlock_irqrestore(&mgr->lock, flags); 196} 197 198void emu10k1_dsp_irqhandler(struct emu10k1_card *card) 199{ 200 unsigned long flags; 201 202 if (card->pt.state != PT_STATE_INACTIVE) { 203 u32 bc; 204 bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0); 205 if (bc != 0) { 206 DPD(3, "pt interrupt, bc = %d\n", bc); 207 spin_lock_irqsave(&card->pt.lock, flags); 208 card->pt.blocks_played = bc; 209 if (card->pt.blocks_played >= card->pt.blocks_copied) { 210 DPF(1, "buffer underrun in passthrough playback\n"); 211 emu10k1_pt_stop(card); 212 } 213 wake_up_interruptible(&card->pt.wait); 214 spin_unlock_irqrestore(&card->pt.lock, flags); 215 } 216 } 217} 218