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