18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Uros Bizjak <uros@kss-loka.si> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Routines for OPL2/OPL3/OPL4 control 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/nospec.h> 118c2ecf20Sopenharmony_ci#include <sound/opl3.h> 128c2ecf20Sopenharmony_ci#include <sound/asound_fm.h> 138c2ecf20Sopenharmony_ci#include "opl3_voice.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER) 168c2ecf20Sopenharmony_ci#define OPL3_SUPPORT_SYNTH 178c2ecf20Sopenharmony_ci#endif 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * There is 18 possible 2 OP voices 218c2ecf20Sopenharmony_ci * (9 in the left and 9 in the right). 228c2ecf20Sopenharmony_ci * The first OP is the modulator and 2nd is the carrier. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * The first three voices in the both sides may be connected 258c2ecf20Sopenharmony_ci * with another voice to a 4 OP voice. For example voice 0 268c2ecf20Sopenharmony_ci * can be connected with voice 3. The operators of voice 3 are 278c2ecf20Sopenharmony_ci * used as operators 3 and 4 of the new 4 OP voice. 288c2ecf20Sopenharmony_ci * In this case the 2 OP voice number 0 is the 'first half' and 298c2ecf20Sopenharmony_ci * voice 3 is the second. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Register offset table for OPL2/3 voices, 358c2ecf20Sopenharmony_ci * OPL2 / one OPL3 register array side only 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cichar snd_opl3_regmap[MAX_OPL2_VOICES][4] = 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci/* OP1 OP2 OP3 OP4 */ 418c2ecf20Sopenharmony_ci/* ------------------------ */ 428c2ecf20Sopenharmony_ci { 0x00, 0x03, 0x08, 0x0b }, 438c2ecf20Sopenharmony_ci { 0x01, 0x04, 0x09, 0x0c }, 448c2ecf20Sopenharmony_ci { 0x02, 0x05, 0x0a, 0x0d }, 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci { 0x08, 0x0b, 0x00, 0x00 }, 478c2ecf20Sopenharmony_ci { 0x09, 0x0c, 0x00, 0x00 }, 488c2ecf20Sopenharmony_ci { 0x0a, 0x0d, 0x00, 0x00 }, 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci { 0x10, 0x13, 0x00, 0x00 }, /* used by percussive voices */ 518c2ecf20Sopenharmony_ci { 0x11, 0x14, 0x00, 0x00 }, /* if the percussive mode */ 528c2ecf20Sopenharmony_ci { 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */ 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_regmap); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * prototypes 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note); 618c2ecf20Sopenharmony_cistatic int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * voice); 628c2ecf20Sopenharmony_cistatic int snd_opl3_set_params(struct snd_opl3 * opl3, struct snd_dm_fm_params * params); 638c2ecf20Sopenharmony_cistatic int snd_opl3_set_mode(struct snd_opl3 * opl3, int mode); 648c2ecf20Sopenharmony_cistatic int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* ------------------------------ */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * open the device exclusively 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ciint snd_opl3_open(struct snd_hwdep * hw, struct file *file) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * ioctl for hwdep device: 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ciint snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file, 808c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct snd_opl3 *opl3 = hw->private_data; 838c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (snd_BUG_ON(!opl3)) 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci switch (cmd) { 898c2ecf20Sopenharmony_ci /* get information */ 908c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_INFO: 918c2ecf20Sopenharmony_ci { 928c2ecf20Sopenharmony_ci struct snd_dm_fm_info info; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci info.fm_mode = opl3->fm_mode; 978c2ecf20Sopenharmony_ci info.rhythm = opl3->rhythm; 988c2ecf20Sopenharmony_ci if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info))) 998c2ecf20Sopenharmony_ci return -EFAULT; 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_RESET: 1048c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 1058c2ecf20Sopenharmony_ci case SNDRV_DM_FM_OSS_IOCTL_RESET: 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci snd_opl3_reset(opl3); 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_PLAY_NOTE: 1118c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 1128c2ecf20Sopenharmony_ci case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE: 1138c2ecf20Sopenharmony_ci#endif 1148c2ecf20Sopenharmony_ci { 1158c2ecf20Sopenharmony_ci struct snd_dm_fm_note note; 1168c2ecf20Sopenharmony_ci if (copy_from_user(¬e, argp, sizeof(struct snd_dm_fm_note))) 1178c2ecf20Sopenharmony_ci return -EFAULT; 1188c2ecf20Sopenharmony_ci return snd_opl3_play_note(opl3, ¬e); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_SET_VOICE: 1228c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 1238c2ecf20Sopenharmony_ci case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE: 1248c2ecf20Sopenharmony_ci#endif 1258c2ecf20Sopenharmony_ci { 1268c2ecf20Sopenharmony_ci struct snd_dm_fm_voice voice; 1278c2ecf20Sopenharmony_ci if (copy_from_user(&voice, argp, sizeof(struct snd_dm_fm_voice))) 1288c2ecf20Sopenharmony_ci return -EFAULT; 1298c2ecf20Sopenharmony_ci return snd_opl3_set_voice(opl3, &voice); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_SET_PARAMS: 1338c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 1348c2ecf20Sopenharmony_ci case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS: 1358c2ecf20Sopenharmony_ci#endif 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci struct snd_dm_fm_params params; 1388c2ecf20Sopenharmony_ci if (copy_from_user(¶ms, argp, sizeof(struct snd_dm_fm_params))) 1398c2ecf20Sopenharmony_ci return -EFAULT; 1408c2ecf20Sopenharmony_ci return snd_opl3_set_params(opl3, ¶ms); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_SET_MODE: 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 1458c2ecf20Sopenharmony_ci case SNDRV_DM_FM_OSS_IOCTL_SET_MODE: 1468c2ecf20Sopenharmony_ci#endif 1478c2ecf20Sopenharmony_ci return snd_opl3_set_mode(opl3, (int) arg); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_SET_CONNECTION: 1508c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 1518c2ecf20Sopenharmony_ci case SNDRV_DM_FM_OSS_IOCTL_SET_OPL: 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci return snd_opl3_set_connection(opl3, (int) arg); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#ifdef OPL3_SUPPORT_SYNTH 1568c2ecf20Sopenharmony_ci case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES: 1578c2ecf20Sopenharmony_ci snd_opl3_clear_patches(opl3); 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci#endif 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 1628c2ecf20Sopenharmony_ci default: 1638c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "unknown IOCTL: 0x%x\n", cmd); 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci return -ENOTTY; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * close the device 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ciint snd_opl3_release(struct snd_hwdep * hw, struct file *file) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct snd_opl3 *opl3 = hw->private_data; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci snd_opl3_reset(opl3); 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#ifdef OPL3_SUPPORT_SYNTH 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * write the device - load patches 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cilong snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, 1858c2ecf20Sopenharmony_ci loff_t *offset) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct snd_opl3 *opl3 = hw->private_data; 1888c2ecf20Sopenharmony_ci long result = 0; 1898c2ecf20Sopenharmony_ci int err = 0; 1908c2ecf20Sopenharmony_ci struct sbi_patch inst; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci while (count >= sizeof(inst)) { 1938c2ecf20Sopenharmony_ci unsigned char type; 1948c2ecf20Sopenharmony_ci if (copy_from_user(&inst, buf, sizeof(inst))) 1958c2ecf20Sopenharmony_ci return -EFAULT; 1968c2ecf20Sopenharmony_ci if (!memcmp(inst.key, FM_KEY_SBI, 4) || 1978c2ecf20Sopenharmony_ci !memcmp(inst.key, FM_KEY_2OP, 4)) 1988c2ecf20Sopenharmony_ci type = FM_PATCH_OPL2; 1998c2ecf20Sopenharmony_ci else if (!memcmp(inst.key, FM_KEY_4OP, 4)) 2008c2ecf20Sopenharmony_ci type = FM_PATCH_OPL3; 2018c2ecf20Sopenharmony_ci else /* invalid type */ 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type, 2048c2ecf20Sopenharmony_ci inst.name, inst.extension, 2058c2ecf20Sopenharmony_ci inst.data); 2068c2ecf20Sopenharmony_ci if (err < 0) 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci result += sizeof(inst); 2098c2ecf20Sopenharmony_ci count -= sizeof(inst); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci return result > 0 ? result : err; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * Patch management 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* offsets for SBI params */ 2208c2ecf20Sopenharmony_ci#define AM_VIB 0 2218c2ecf20Sopenharmony_ci#define KSL_LEVEL 2 2228c2ecf20Sopenharmony_ci#define ATTACK_DECAY 4 2238c2ecf20Sopenharmony_ci#define SUSTAIN_RELEASE 6 2248c2ecf20Sopenharmony_ci#define WAVE_SELECT 8 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* offset for SBI instrument */ 2278c2ecf20Sopenharmony_ci#define CONNECTION 10 2288c2ecf20Sopenharmony_ci#define OFFSET_4OP 11 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * load a patch, obviously. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * loaded on the given program and bank numbers with the given type 2348c2ecf20Sopenharmony_ci * (FM_PATCH_OPLx). 2358c2ecf20Sopenharmony_ci * data is the pointer of SBI record _without_ header (key and name). 2368c2ecf20Sopenharmony_ci * name is the name string of the patch. 2378c2ecf20Sopenharmony_ci * ext is the extension data of 7 bytes long (stored in name of SBI 2388c2ecf20Sopenharmony_ci * data up to offset 25), or NULL to skip. 2398c2ecf20Sopenharmony_ci * return 0 if successful or a negative error code. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ciint snd_opl3_load_patch(struct snd_opl3 *opl3, 2428c2ecf20Sopenharmony_ci int prog, int bank, int type, 2438c2ecf20Sopenharmony_ci const char *name, 2448c2ecf20Sopenharmony_ci const unsigned char *ext, 2458c2ecf20Sopenharmony_ci const unsigned char *data) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct fm_patch *patch; 2488c2ecf20Sopenharmony_ci int i; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci patch = snd_opl3_find_patch(opl3, prog, bank, 1); 2518c2ecf20Sopenharmony_ci if (!patch) 2528c2ecf20Sopenharmony_ci return -ENOMEM; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci patch->type = type; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 2578c2ecf20Sopenharmony_ci patch->inst.op[i].am_vib = data[AM_VIB + i]; 2588c2ecf20Sopenharmony_ci patch->inst.op[i].ksl_level = data[KSL_LEVEL + i]; 2598c2ecf20Sopenharmony_ci patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i]; 2608c2ecf20Sopenharmony_ci patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i]; 2618c2ecf20Sopenharmony_ci patch->inst.op[i].wave_select = data[WAVE_SELECT + i]; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci patch->inst.feedback_connection[0] = data[CONNECTION]; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (type == FM_PATCH_OPL3) { 2668c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 2678c2ecf20Sopenharmony_ci patch->inst.op[i+2].am_vib = 2688c2ecf20Sopenharmony_ci data[OFFSET_4OP + AM_VIB + i]; 2698c2ecf20Sopenharmony_ci patch->inst.op[i+2].ksl_level = 2708c2ecf20Sopenharmony_ci data[OFFSET_4OP + KSL_LEVEL + i]; 2718c2ecf20Sopenharmony_ci patch->inst.op[i+2].attack_decay = 2728c2ecf20Sopenharmony_ci data[OFFSET_4OP + ATTACK_DECAY + i]; 2738c2ecf20Sopenharmony_ci patch->inst.op[i+2].sustain_release = 2748c2ecf20Sopenharmony_ci data[OFFSET_4OP + SUSTAIN_RELEASE + i]; 2758c2ecf20Sopenharmony_ci patch->inst.op[i+2].wave_select = 2768c2ecf20Sopenharmony_ci data[OFFSET_4OP + WAVE_SELECT + i]; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci patch->inst.feedback_connection[1] = 2798c2ecf20Sopenharmony_ci data[OFFSET_4OP + CONNECTION]; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (ext) { 2838c2ecf20Sopenharmony_ci patch->inst.echo_delay = ext[0]; 2848c2ecf20Sopenharmony_ci patch->inst.echo_atten = ext[1]; 2858c2ecf20Sopenharmony_ci patch->inst.chorus_spread = ext[2]; 2868c2ecf20Sopenharmony_ci patch->inst.trnsps = ext[3]; 2878c2ecf20Sopenharmony_ci patch->inst.fix_dur = ext[4]; 2888c2ecf20Sopenharmony_ci patch->inst.modes = ext[5]; 2898c2ecf20Sopenharmony_ci patch->inst.fix_key = ext[6]; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (name) 2938c2ecf20Sopenharmony_ci strlcpy(patch->name, name, sizeof(patch->name)); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_load_patch); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * find a patch with the given program and bank numbers, returns its pointer 3018c2ecf20Sopenharmony_ci * if no matching patch is found and create_patch is set, it creates a 3028c2ecf20Sopenharmony_ci * new patch object. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistruct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank, 3058c2ecf20Sopenharmony_ci int create_patch) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci /* pretty dumb hash key */ 3088c2ecf20Sopenharmony_ci unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE; 3098c2ecf20Sopenharmony_ci struct fm_patch *patch; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci for (patch = opl3->patch_table[key]; patch; patch = patch->next) { 3128c2ecf20Sopenharmony_ci if (patch->prog == prog && patch->bank == bank) 3138c2ecf20Sopenharmony_ci return patch; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci if (!create_patch) 3168c2ecf20Sopenharmony_ci return NULL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci patch = kzalloc(sizeof(*patch), GFP_KERNEL); 3198c2ecf20Sopenharmony_ci if (!patch) 3208c2ecf20Sopenharmony_ci return NULL; 3218c2ecf20Sopenharmony_ci patch->prog = prog; 3228c2ecf20Sopenharmony_ci patch->bank = bank; 3238c2ecf20Sopenharmony_ci patch->next = opl3->patch_table[key]; 3248c2ecf20Sopenharmony_ci opl3->patch_table[key] = patch; 3258c2ecf20Sopenharmony_ci return patch; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_find_patch); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* 3308c2ecf20Sopenharmony_ci * Clear all patches of the given OPL3 instance 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_civoid snd_opl3_clear_patches(struct snd_opl3 *opl3) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int i; 3358c2ecf20Sopenharmony_ci for (i = 0; i < OPL3_PATCH_HASH_SIZE; i++) { 3368c2ecf20Sopenharmony_ci struct fm_patch *patch, *next; 3378c2ecf20Sopenharmony_ci for (patch = opl3->patch_table[i]; patch; patch = next) { 3388c2ecf20Sopenharmony_ci next = patch->next; 3398c2ecf20Sopenharmony_ci kfree(patch); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci memset(opl3->patch_table, 0, sizeof(opl3->patch_table)); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci#endif /* OPL3_SUPPORT_SYNTH */ 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* ------------------------------ */ 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_civoid snd_opl3_reset(struct snd_opl3 * opl3) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci unsigned short opl3_reg; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci unsigned short reg_side; 3538c2ecf20Sopenharmony_ci unsigned char voice_offset; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci int max_voices, i; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci max_voices = (opl3->hardware < OPL3_HW_OPL3) ? 3588c2ecf20Sopenharmony_ci MAX_OPL2_VOICES : MAX_OPL3_VOICES; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci for (i = 0; i < max_voices; i++) { 3618c2ecf20Sopenharmony_ci /* Get register array side and offset of voice */ 3628c2ecf20Sopenharmony_ci if (i < MAX_OPL2_VOICES) { 3638c2ecf20Sopenharmony_ci /* Left register block for voices 0 .. 8 */ 3648c2ecf20Sopenharmony_ci reg_side = OPL3_LEFT; 3658c2ecf20Sopenharmony_ci voice_offset = i; 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci /* Right register block for voices 9 .. 17 */ 3688c2ecf20Sopenharmony_ci reg_side = OPL3_RIGHT; 3698c2ecf20Sopenharmony_ci voice_offset = i - MAX_OPL2_VOICES; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]); 3728c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */ 3738c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]); 3748c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */ 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); 3778c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, 0x00); /* Note off */ 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci opl3->max_voices = MAX_OPL2_VOICES; 3818c2ecf20Sopenharmony_ci opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); 3848c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ 3858c2ecf20Sopenharmony_ci opl3->rhythm = 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_opl3_reset); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci unsigned short reg_side; 3938c2ecf20Sopenharmony_ci unsigned char voice_offset; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci unsigned short opl3_reg; 3968c2ecf20Sopenharmony_ci unsigned char reg_val; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Voices 0 - 8 in OPL2 mode */ 3998c2ecf20Sopenharmony_ci /* Voices 0 - 17 in OPL3 mode */ 4008c2ecf20Sopenharmony_ci if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? 4018c2ecf20Sopenharmony_ci MAX_OPL3_VOICES : MAX_OPL2_VOICES)) 4028c2ecf20Sopenharmony_ci return -EINVAL; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Get register array side and offset of voice */ 4058c2ecf20Sopenharmony_ci if (note->voice < MAX_OPL2_VOICES) { 4068c2ecf20Sopenharmony_ci /* Left register block for voices 0 .. 8 */ 4078c2ecf20Sopenharmony_ci reg_side = OPL3_LEFT; 4088c2ecf20Sopenharmony_ci voice_offset = note->voice; 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci /* Right register block for voices 9 .. 17 */ 4118c2ecf20Sopenharmony_ci reg_side = OPL3_RIGHT; 4128c2ecf20Sopenharmony_ci voice_offset = note->voice - MAX_OPL2_VOICES; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Set lower 8 bits of note frequency */ 4168c2ecf20Sopenharmony_ci reg_val = (unsigned char) note->fnum; 4178c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); 4188c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci reg_val = 0x00; 4218c2ecf20Sopenharmony_ci /* Set output sound flag */ 4228c2ecf20Sopenharmony_ci if (note->key_on) 4238c2ecf20Sopenharmony_ci reg_val |= OPL3_KEYON_BIT; 4248c2ecf20Sopenharmony_ci /* Set octave */ 4258c2ecf20Sopenharmony_ci reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK; 4268c2ecf20Sopenharmony_ci /* Set higher 2 bits of note frequency */ 4278c2ecf20Sopenharmony_ci reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Set OPL3 KEYON_BLOCK register of requested voice */ 4308c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); 4318c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * voice) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci unsigned short reg_side; 4408c2ecf20Sopenharmony_ci unsigned char op_offset; 4418c2ecf20Sopenharmony_ci unsigned char voice_offset, voice_op; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci unsigned short opl3_reg; 4448c2ecf20Sopenharmony_ci unsigned char reg_val; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Only operators 1 and 2 */ 4478c2ecf20Sopenharmony_ci if (voice->op > 1) 4488c2ecf20Sopenharmony_ci return -EINVAL; 4498c2ecf20Sopenharmony_ci /* Voices 0 - 8 in OPL2 mode */ 4508c2ecf20Sopenharmony_ci /* Voices 0 - 17 in OPL3 mode */ 4518c2ecf20Sopenharmony_ci if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? 4528c2ecf20Sopenharmony_ci MAX_OPL3_VOICES : MAX_OPL2_VOICES)) 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Get register array side and offset of voice */ 4568c2ecf20Sopenharmony_ci if (voice->voice < MAX_OPL2_VOICES) { 4578c2ecf20Sopenharmony_ci /* Left register block for voices 0 .. 8 */ 4588c2ecf20Sopenharmony_ci reg_side = OPL3_LEFT; 4598c2ecf20Sopenharmony_ci voice_offset = voice->voice; 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci /* Right register block for voices 9 .. 17 */ 4628c2ecf20Sopenharmony_ci reg_side = OPL3_RIGHT; 4638c2ecf20Sopenharmony_ci voice_offset = voice->voice - MAX_OPL2_VOICES; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci /* Get register offset of operator */ 4668c2ecf20Sopenharmony_ci voice_offset = array_index_nospec(voice_offset, MAX_OPL2_VOICES); 4678c2ecf20Sopenharmony_ci voice_op = array_index_nospec(voice->op, 4); 4688c2ecf20Sopenharmony_ci op_offset = snd_opl3_regmap[voice_offset][voice_op]; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci reg_val = 0x00; 4718c2ecf20Sopenharmony_ci /* Set amplitude modulation (tremolo) effect */ 4728c2ecf20Sopenharmony_ci if (voice->am) 4738c2ecf20Sopenharmony_ci reg_val |= OPL3_TREMOLO_ON; 4748c2ecf20Sopenharmony_ci /* Set vibrato effect */ 4758c2ecf20Sopenharmony_ci if (voice->vibrato) 4768c2ecf20Sopenharmony_ci reg_val |= OPL3_VIBRATO_ON; 4778c2ecf20Sopenharmony_ci /* Set sustaining sound phase */ 4788c2ecf20Sopenharmony_ci if (voice->do_sustain) 4798c2ecf20Sopenharmony_ci reg_val |= OPL3_SUSTAIN_ON; 4808c2ecf20Sopenharmony_ci /* Set keyboard scaling bit */ 4818c2ecf20Sopenharmony_ci if (voice->kbd_scale) 4828c2ecf20Sopenharmony_ci reg_val |= OPL3_KSR; 4838c2ecf20Sopenharmony_ci /* Set harmonic or frequency multiplier */ 4848c2ecf20Sopenharmony_ci reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* Set OPL3 AM_VIB register of requested voice/operator */ 4878c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); 4888c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Set decreasing volume of higher notes */ 4918c2ecf20Sopenharmony_ci reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK; 4928c2ecf20Sopenharmony_ci /* Set output volume */ 4938c2ecf20Sopenharmony_ci reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Set OPL3 KSL_LEVEL register of requested voice/operator */ 4968c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); 4978c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* Set attack phase level */ 5008c2ecf20Sopenharmony_ci reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK; 5018c2ecf20Sopenharmony_ci /* Set decay phase level */ 5028c2ecf20Sopenharmony_ci reg_val |= voice->decay & OPL3_DECAY_MASK; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ 5058c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); 5068c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Set sustain phase level */ 5098c2ecf20Sopenharmony_ci reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK; 5108c2ecf20Sopenharmony_ci /* Set release phase level */ 5118c2ecf20Sopenharmony_ci reg_val |= voice->release & OPL3_RELEASE_MASK; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ 5148c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); 5158c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Set inter-operator feedback */ 5188c2ecf20Sopenharmony_ci reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK; 5198c2ecf20Sopenharmony_ci /* Set inter-operator connection */ 5208c2ecf20Sopenharmony_ci if (voice->connection) 5218c2ecf20Sopenharmony_ci reg_val |= OPL3_CONNECTION_BIT; 5228c2ecf20Sopenharmony_ci /* OPL-3 only */ 5238c2ecf20Sopenharmony_ci if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) { 5248c2ecf20Sopenharmony_ci if (voice->left) 5258c2ecf20Sopenharmony_ci reg_val |= OPL3_VOICE_TO_LEFT; 5268c2ecf20Sopenharmony_ci if (voice->right) 5278c2ecf20Sopenharmony_ci reg_val |= OPL3_VOICE_TO_RIGHT; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci /* Feedback/connection bits are applicable to voice */ 5308c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); 5318c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Select waveform */ 5348c2ecf20Sopenharmony_ci reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK; 5358c2ecf20Sopenharmony_ci opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); 5368c2ecf20Sopenharmony_ci opl3->command(opl3, opl3_reg, reg_val); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int snd_opl3_set_params(struct snd_opl3 * opl3, struct snd_dm_fm_params * params) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci unsigned char reg_val; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci reg_val = 0x00; 5468c2ecf20Sopenharmony_ci /* Set keyboard split method */ 5478c2ecf20Sopenharmony_ci if (params->kbd_split) 5488c2ecf20Sopenharmony_ci reg_val |= OPL3_KEYBOARD_SPLIT; 5498c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci reg_val = 0x00; 5528c2ecf20Sopenharmony_ci /* Set amplitude modulation (tremolo) depth */ 5538c2ecf20Sopenharmony_ci if (params->am_depth) 5548c2ecf20Sopenharmony_ci reg_val |= OPL3_TREMOLO_DEPTH; 5558c2ecf20Sopenharmony_ci /* Set vibrato depth */ 5568c2ecf20Sopenharmony_ci if (params->vib_depth) 5578c2ecf20Sopenharmony_ci reg_val |= OPL3_VIBRATO_DEPTH; 5588c2ecf20Sopenharmony_ci /* Set percussion mode */ 5598c2ecf20Sopenharmony_ci if (params->rhythm) { 5608c2ecf20Sopenharmony_ci reg_val |= OPL3_PERCUSSION_ENABLE; 5618c2ecf20Sopenharmony_ci opl3->rhythm = 1; 5628c2ecf20Sopenharmony_ci } else { 5638c2ecf20Sopenharmony_ci opl3->rhythm = 0; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci /* Play percussion instruments */ 5668c2ecf20Sopenharmony_ci if (params->bass) 5678c2ecf20Sopenharmony_ci reg_val |= OPL3_BASSDRUM_ON; 5688c2ecf20Sopenharmony_ci if (params->snare) 5698c2ecf20Sopenharmony_ci reg_val |= OPL3_SNAREDRUM_ON; 5708c2ecf20Sopenharmony_ci if (params->tomtom) 5718c2ecf20Sopenharmony_ci reg_val |= OPL3_TOMTOM_ON; 5728c2ecf20Sopenharmony_ci if (params->cymbal) 5738c2ecf20Sopenharmony_ci reg_val |= OPL3_CYMBAL_ON; 5748c2ecf20Sopenharmony_ci if (params->hihat) 5758c2ecf20Sopenharmony_ci reg_val |= OPL3_HIHAT_ON; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val); 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int snd_opl3_set_mode(struct snd_opl3 * opl3, int mode) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3)) 5848c2ecf20Sopenharmony_ci return -EINVAL; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci opl3->fm_mode = mode; 5878c2ecf20Sopenharmony_ci if (opl3->hardware >= OPL3_HW_OPL3) 5888c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00); /* Clear 4-op connections */ 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci unsigned char reg_val; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* OPL-3 only */ 5988c2ecf20Sopenharmony_ci if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3) 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 | 6028c2ecf20Sopenharmony_ci OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2); 6038c2ecf20Sopenharmony_ci /* Set 4-op connections */ 6048c2ecf20Sopenharmony_ci opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 609