diff options
author | Takashi Iwai <tiwai@suse.de> | 2007-10-30 06:49:22 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-01-31 11:29:13 -0500 |
commit | 224a033252bba46c5c8b5df625f5e781ca138f48 (patch) | |
tree | 11bb0ad9a3bab736091c73bb46b79d42ee0ff34d /sound/drivers/opl3/opl3_synth.c | |
parent | ceac4bf34e14d9040d16b35fd97a92d6e951ccf4 (diff) |
[ALSA] opl3 - Use hwdep for patch loading
Use the hwdep device for loading OPL2/3 patch data instead of the
messy sequencer instrument layer.
Due to this change, the sbiload program should be updated, too.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/drivers/opl3/opl3_synth.c')
-rw-r--r-- | sound/drivers/opl3/opl3_synth.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index a4b3543a7118..d55eefce44c1 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c | |||
@@ -165,6 +165,10 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file, | |||
165 | #endif | 165 | #endif |
166 | return snd_opl3_set_connection(opl3, (int) arg); | 166 | return snd_opl3_set_connection(opl3, (int) arg); |
167 | 167 | ||
168 | case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES: | ||
169 | snd_opl3_clear_patches(opl3); | ||
170 | return 0; | ||
171 | |||
168 | #ifdef CONFIG_SND_DEBUG | 172 | #ifdef CONFIG_SND_DEBUG |
169 | default: | 173 | default: |
170 | snd_printk("unknown IOCTL: 0x%x\n", cmd); | 174 | snd_printk("unknown IOCTL: 0x%x\n", cmd); |
@@ -188,6 +192,170 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file) | |||
188 | return 0; | 192 | return 0; |
189 | } | 193 | } |
190 | 194 | ||
195 | /* | ||
196 | * write the device - load patches | ||
197 | */ | ||
198 | long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, | ||
199 | loff_t *offset) | ||
200 | { | ||
201 | struct snd_opl3 *opl3 = hw->private_data; | ||
202 | long result = 0; | ||
203 | int err = 0; | ||
204 | struct sbi_patch inst; | ||
205 | |||
206 | while (count >= sizeof(inst)) { | ||
207 | unsigned char type; | ||
208 | if (copy_from_user(&inst, buf, sizeof(inst))) | ||
209 | return -EFAULT; | ||
210 | if (!memcmp(inst.key, FM_KEY_SBI, 4) || | ||
211 | !memcmp(inst.key, FM_KEY_2OP, 4)) | ||
212 | type = FM_PATCH_OPL2; | ||
213 | else if (!memcmp(inst.key, FM_KEY_4OP, 4)) | ||
214 | type = FM_PATCH_OPL3; | ||
215 | else /* invalid type */ | ||
216 | break; | ||
217 | err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type, | ||
218 | inst.name, inst.extension, | ||
219 | inst.data); | ||
220 | if (err < 0) | ||
221 | break; | ||
222 | result += sizeof(inst); | ||
223 | count -= sizeof(inst); | ||
224 | } | ||
225 | return result > 0 ? result : err; | ||
226 | } | ||
227 | |||
228 | |||
229 | /* | ||
230 | * Patch management | ||
231 | */ | ||
232 | |||
233 | /* offsets for SBI params */ | ||
234 | #define AM_VIB 0 | ||
235 | #define KSL_LEVEL 2 | ||
236 | #define ATTACK_DECAY 4 | ||
237 | #define SUSTAIN_RELEASE 6 | ||
238 | #define WAVE_SELECT 8 | ||
239 | |||
240 | /* offset for SBI instrument */ | ||
241 | #define CONNECTION 10 | ||
242 | #define OFFSET_4OP 11 | ||
243 | |||
244 | /* | ||
245 | * load a patch, obviously. | ||
246 | * | ||
247 | * loaded on the given program and bank numbers with the given type | ||
248 | * (FM_PATCH_OPLx). | ||
249 | * data is the pointer of SBI record _without_ header (key and name). | ||
250 | * name is the name string of the patch. | ||
251 | * ext is the extension data of 7 bytes long (stored in name of SBI | ||
252 | * data up to offset 25), or NULL to skip. | ||
253 | * return 0 if successful or a negative error code. | ||
254 | */ | ||
255 | int snd_opl3_load_patch(struct snd_opl3 *opl3, | ||
256 | int prog, int bank, int type, | ||
257 | const char *name, | ||
258 | const unsigned char *ext, | ||
259 | const unsigned char *data) | ||
260 | { | ||
261 | struct fm_patch *patch; | ||
262 | int i; | ||
263 | |||
264 | patch = snd_opl3_find_patch(opl3, prog, bank, 1); | ||
265 | if (!patch) | ||
266 | return -ENOMEM; | ||
267 | |||
268 | patch->type = type; | ||
269 | |||
270 | for (i = 0; i < 2; i++) { | ||
271 | patch->inst.op[i].am_vib = data[AM_VIB + i]; | ||
272 | patch->inst.op[i].ksl_level = data[KSL_LEVEL + i]; | ||
273 | patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i]; | ||
274 | patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i]; | ||
275 | patch->inst.op[i].wave_select = data[WAVE_SELECT + i]; | ||
276 | } | ||
277 | patch->inst.feedback_connection[0] = data[CONNECTION]; | ||
278 | |||
279 | if (type == FM_PATCH_OPL3) { | ||
280 | for (i = 0; i < 2; i++) { | ||
281 | patch->inst.op[i+2].am_vib = | ||
282 | data[OFFSET_4OP + AM_VIB + i]; | ||
283 | patch->inst.op[i+2].ksl_level = | ||
284 | data[OFFSET_4OP + KSL_LEVEL + i]; | ||
285 | patch->inst.op[i+2].attack_decay = | ||
286 | data[OFFSET_4OP + ATTACK_DECAY + i]; | ||
287 | patch->inst.op[i+2].sustain_release = | ||
288 | data[OFFSET_4OP + SUSTAIN_RELEASE + i]; | ||
289 | patch->inst.op[i+2].wave_select = | ||
290 | data[OFFSET_4OP + WAVE_SELECT + i]; | ||
291 | } | ||
292 | patch->inst.feedback_connection[1] = | ||
293 | data[OFFSET_4OP + CONNECTION]; | ||
294 | } | ||
295 | |||
296 | if (ext) { | ||
297 | patch->inst.echo_delay = ext[0]; | ||
298 | patch->inst.echo_atten = ext[1]; | ||
299 | patch->inst.chorus_spread = ext[2]; | ||
300 | patch->inst.trnsps = ext[3]; | ||
301 | patch->inst.fix_dur = ext[4]; | ||
302 | patch->inst.modes = ext[5]; | ||
303 | patch->inst.fix_key = ext[6]; | ||
304 | } | ||
305 | |||
306 | if (name) | ||
307 | strlcpy(patch->name, name, sizeof(patch->name)); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | EXPORT_SYMBOL(snd_opl3_load_patch); | ||
312 | |||
313 | /* | ||
314 | * find a patch with the given program and bank numbers, returns its pointer | ||
315 | * if no matching patch is found and create_patch is set, it creates a | ||
316 | * new patch object. | ||
317 | */ | ||
318 | struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank, | ||
319 | int create_patch) | ||
320 | { | ||
321 | /* pretty dumb hash key */ | ||
322 | unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE; | ||
323 | struct fm_patch *patch; | ||
324 | |||
325 | for (patch = opl3->patch_table[key]; patch; patch = patch->next) { | ||
326 | if (patch->prog == prog && patch->bank == bank) | ||
327 | return patch; | ||
328 | } | ||
329 | if (!create_patch) | ||
330 | return NULL; | ||
331 | |||
332 | patch = kzalloc(sizeof(*patch), GFP_KERNEL); | ||
333 | if (!patch) | ||
334 | return NULL; | ||
335 | patch->prog = prog; | ||
336 | patch->bank = bank; | ||
337 | patch->next = opl3->patch_table[key]; | ||
338 | opl3->patch_table[key] = patch; | ||
339 | return patch; | ||
340 | } | ||
341 | EXPORT_SYMBOL(snd_opl3_find_patch); | ||
342 | |||
343 | /* | ||
344 | * Clear all patches of the given OPL3 instance | ||
345 | */ | ||
346 | void snd_opl3_clear_patches(struct snd_opl3 *opl3) | ||
347 | { | ||
348 | int i; | ||
349 | for (i = 0; i < OPL3_PATCH_HASH_SIZE; i++) { | ||
350 | struct fm_patch *patch, *next; | ||
351 | for (patch = opl3->patch_table[i]; patch; patch = next) { | ||
352 | next = patch->next; | ||
353 | kfree(patch); | ||
354 | } | ||
355 | } | ||
356 | memset(opl3->patch_table, 0, sizeof(opl3->patch_table)); | ||
357 | } | ||
358 | |||
191 | /* ------------------------------ */ | 359 | /* ------------------------------ */ |
192 | 360 | ||
193 | void snd_opl3_reset(struct snd_opl3 * opl3) | 361 | void snd_opl3_reset(struct snd_opl3 * opl3) |