cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

opl3_synth.c (16436B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
      4 *                   
      5 *  Routines for OPL2/OPL3/OPL4 control
      6 */
      7
      8#include <linux/slab.h>
      9#include <linux/export.h>
     10#include <linux/nospec.h>
     11#include <sound/opl3.h>
     12#include <sound/asound_fm.h>
     13#include "opl3_voice.h"
     14
     15#if IS_ENABLED(CONFIG_SND_SEQUENCER)
     16#define OPL3_SUPPORT_SYNTH
     17#endif
     18
     19/*
     20 *    There is 18 possible 2 OP voices
     21 *      (9 in the left and 9 in the right).
     22 *      The first OP is the modulator and 2nd is the carrier.
     23 *
     24 *      The first three voices in the both sides may be connected
     25 *      with another voice to a 4 OP voice. For example voice 0
     26 *      can be connected with voice 3. The operators of voice 3 are
     27 *      used as operators 3 and 4 of the new 4 OP voice.
     28 *      In this case the 2 OP voice number 0 is the 'first half' and
     29 *      voice 3 is the second.
     30 */
     31
     32
     33/*
     34 *    Register offset table for OPL2/3 voices,
     35 *    OPL2 / one OPL3 register array side only
     36 */
     37
     38char snd_opl3_regmap[MAX_OPL2_VOICES][4] =
     39{
     40/*	  OP1   OP2   OP3   OP4		*/
     41/*	 ------------------------	*/
     42	{ 0x00, 0x03, 0x08, 0x0b },
     43	{ 0x01, 0x04, 0x09, 0x0c },
     44	{ 0x02, 0x05, 0x0a, 0x0d },
     45
     46	{ 0x08, 0x0b, 0x00, 0x00 },
     47	{ 0x09, 0x0c, 0x00, 0x00 },
     48	{ 0x0a, 0x0d, 0x00, 0x00 },
     49
     50	{ 0x10, 0x13, 0x00, 0x00 },	/* used by percussive voices */
     51	{ 0x11, 0x14, 0x00, 0x00 },	/* if the percussive mode */
     52	{ 0x12, 0x15, 0x00, 0x00 }	/* is selected (only left reg block) */
     53};
     54
     55EXPORT_SYMBOL(snd_opl3_regmap);
     56
     57/*
     58 * prototypes
     59 */
     60static int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note);
     61static int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * voice);
     62static int snd_opl3_set_params(struct snd_opl3 * opl3, struct snd_dm_fm_params * params);
     63static int snd_opl3_set_mode(struct snd_opl3 * opl3, int mode);
     64static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection);
     65
     66/* ------------------------------ */
     67
     68/*
     69 * open the device exclusively
     70 */
     71int snd_opl3_open(struct snd_hwdep * hw, struct file *file)
     72{
     73	return 0;
     74}
     75
     76/*
     77 * ioctl for hwdep device:
     78 */
     79int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
     80		   unsigned int cmd, unsigned long arg)
     81{
     82	struct snd_opl3 *opl3 = hw->private_data;
     83	void __user *argp = (void __user *)arg;
     84
     85	if (snd_BUG_ON(!opl3))
     86		return -EINVAL;
     87
     88	switch (cmd) {
     89		/* get information */
     90	case SNDRV_DM_FM_IOCTL_INFO:
     91		{
     92			struct snd_dm_fm_info info;
     93
     94			memset(&info, 0, sizeof(info));
     95
     96			info.fm_mode = opl3->fm_mode;
     97			info.rhythm = opl3->rhythm;
     98			if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info)))
     99				return -EFAULT;
    100			return 0;
    101		}
    102
    103	case SNDRV_DM_FM_IOCTL_RESET:
    104#ifdef CONFIG_SND_OSSEMUL
    105	case SNDRV_DM_FM_OSS_IOCTL_RESET:
    106#endif
    107		snd_opl3_reset(opl3);
    108		return 0;
    109
    110	case SNDRV_DM_FM_IOCTL_PLAY_NOTE:
    111#ifdef CONFIG_SND_OSSEMUL
    112	case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE:
    113#endif
    114		{
    115			struct snd_dm_fm_note note;
    116			if (copy_from_user(&note, argp, sizeof(struct snd_dm_fm_note)))
    117				return -EFAULT;
    118			return snd_opl3_play_note(opl3, &note);
    119		}
    120
    121	case SNDRV_DM_FM_IOCTL_SET_VOICE:
    122#ifdef CONFIG_SND_OSSEMUL
    123	case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE:
    124#endif
    125		{
    126			struct snd_dm_fm_voice voice;
    127			if (copy_from_user(&voice, argp, sizeof(struct snd_dm_fm_voice)))
    128				return -EFAULT;
    129			return snd_opl3_set_voice(opl3, &voice);
    130		}
    131
    132	case SNDRV_DM_FM_IOCTL_SET_PARAMS:
    133#ifdef CONFIG_SND_OSSEMUL
    134	case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS:
    135#endif
    136		{
    137			struct snd_dm_fm_params params;
    138			if (copy_from_user(&params, argp, sizeof(struct snd_dm_fm_params)))
    139				return -EFAULT;
    140			return snd_opl3_set_params(opl3, &params);
    141		}
    142
    143	case SNDRV_DM_FM_IOCTL_SET_MODE:
    144#ifdef CONFIG_SND_OSSEMUL
    145	case SNDRV_DM_FM_OSS_IOCTL_SET_MODE:
    146#endif
    147		return snd_opl3_set_mode(opl3, (int) arg);
    148
    149	case SNDRV_DM_FM_IOCTL_SET_CONNECTION:
    150#ifdef CONFIG_SND_OSSEMUL
    151	case SNDRV_DM_FM_OSS_IOCTL_SET_OPL:
    152#endif
    153		return snd_opl3_set_connection(opl3, (int) arg);
    154
    155#ifdef OPL3_SUPPORT_SYNTH
    156	case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES:
    157		snd_opl3_clear_patches(opl3);
    158		return 0;
    159#endif
    160
    161#ifdef CONFIG_SND_DEBUG
    162	default:
    163		snd_printk(KERN_WARNING "unknown IOCTL: 0x%x\n", cmd);
    164#endif
    165	}
    166	return -ENOTTY;
    167}
    168
    169/*
    170 * close the device
    171 */
    172int snd_opl3_release(struct snd_hwdep * hw, struct file *file)
    173{
    174	struct snd_opl3 *opl3 = hw->private_data;
    175
    176	snd_opl3_reset(opl3);
    177	return 0;
    178}
    179
    180#ifdef OPL3_SUPPORT_SYNTH
    181/*
    182 * write the device - load patches
    183 */
    184long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
    185		    loff_t *offset)
    186{
    187	struct snd_opl3 *opl3 = hw->private_data;
    188	long result = 0;
    189	int err = 0;
    190	struct sbi_patch inst;
    191
    192	while (count >= sizeof(inst)) {
    193		unsigned char type;
    194		if (copy_from_user(&inst, buf, sizeof(inst)))
    195			return -EFAULT;
    196		if (!memcmp(inst.key, FM_KEY_SBI, 4) ||
    197		    !memcmp(inst.key, FM_KEY_2OP, 4))
    198			type = FM_PATCH_OPL2;
    199		else if (!memcmp(inst.key, FM_KEY_4OP, 4))
    200			type = FM_PATCH_OPL3;
    201		else /* invalid type */
    202			break;
    203		err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type,
    204					  inst.name, inst.extension,
    205					  inst.data);
    206		if (err < 0)
    207			break;
    208		result += sizeof(inst);
    209		count -= sizeof(inst);
    210	}
    211	return result > 0 ? result : err;
    212}
    213
    214
    215/*
    216 * Patch management
    217 */
    218
    219/* offsets for SBI params */
    220#define AM_VIB		0
    221#define KSL_LEVEL	2
    222#define ATTACK_DECAY	4
    223#define SUSTAIN_RELEASE	6
    224#define WAVE_SELECT	8
    225
    226/* offset for SBI instrument */
    227#define CONNECTION	10
    228#define OFFSET_4OP	11
    229
    230/*
    231 * load a patch, obviously.
    232 *
    233 * loaded on the given program and bank numbers with the given type
    234 * (FM_PATCH_OPLx).
    235 * data is the pointer of SBI record _without_ header (key and name).
    236 * name is the name string of the patch.
    237 * ext is the extension data of 7 bytes long (stored in name of SBI
    238 * data up to offset 25), or NULL to skip.
    239 * return 0 if successful or a negative error code.
    240 */
    241int snd_opl3_load_patch(struct snd_opl3 *opl3,
    242			int prog, int bank, int type,
    243			const char *name,
    244			const unsigned char *ext,
    245			const unsigned char *data)
    246{
    247	struct fm_patch *patch;
    248	int i;
    249
    250	patch = snd_opl3_find_patch(opl3, prog, bank, 1);
    251	if (!patch)
    252		return -ENOMEM;
    253
    254	patch->type = type;
    255
    256	for (i = 0; i < 2; i++) {
    257		patch->inst.op[i].am_vib = data[AM_VIB + i];
    258		patch->inst.op[i].ksl_level = data[KSL_LEVEL + i];
    259		patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i];
    260		patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i];
    261		patch->inst.op[i].wave_select = data[WAVE_SELECT + i];
    262	}
    263	patch->inst.feedback_connection[0] = data[CONNECTION];
    264
    265	if (type == FM_PATCH_OPL3) {
    266		for (i = 0; i < 2; i++) {
    267			patch->inst.op[i+2].am_vib =
    268				data[OFFSET_4OP + AM_VIB + i];
    269			patch->inst.op[i+2].ksl_level =
    270				data[OFFSET_4OP + KSL_LEVEL + i];
    271			patch->inst.op[i+2].attack_decay =
    272				data[OFFSET_4OP + ATTACK_DECAY + i];
    273			patch->inst.op[i+2].sustain_release =
    274				data[OFFSET_4OP + SUSTAIN_RELEASE + i];
    275			patch->inst.op[i+2].wave_select =
    276				data[OFFSET_4OP + WAVE_SELECT + i];
    277		}
    278		patch->inst.feedback_connection[1] =
    279			data[OFFSET_4OP + CONNECTION];
    280	}
    281
    282	if (ext) {
    283		patch->inst.echo_delay = ext[0];
    284		patch->inst.echo_atten = ext[1];
    285		patch->inst.chorus_spread = ext[2];
    286		patch->inst.trnsps = ext[3];
    287		patch->inst.fix_dur = ext[4];
    288		patch->inst.modes = ext[5];
    289		patch->inst.fix_key = ext[6];
    290	}
    291
    292	if (name)
    293		strscpy(patch->name, name, sizeof(patch->name));
    294
    295	return 0;
    296}
    297EXPORT_SYMBOL(snd_opl3_load_patch);
    298
    299/*
    300 * find a patch with the given program and bank numbers, returns its pointer
    301 * if no matching patch is found and create_patch is set, it creates a
    302 * new patch object.
    303 */
    304struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
    305				     int create_patch)
    306{
    307	/* pretty dumb hash key */
    308	unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE;
    309	struct fm_patch *patch;
    310
    311	for (patch = opl3->patch_table[key]; patch; patch = patch->next) {
    312		if (patch->prog == prog && patch->bank == bank)
    313			return patch;
    314	}
    315	if (!create_patch)
    316		return NULL;
    317
    318	patch = kzalloc(sizeof(*patch), GFP_KERNEL);
    319	if (!patch)
    320		return NULL;
    321	patch->prog = prog;
    322	patch->bank = bank;
    323	patch->next = opl3->patch_table[key];
    324	opl3->patch_table[key] = patch;
    325	return patch;
    326}
    327EXPORT_SYMBOL(snd_opl3_find_patch);
    328
    329/*
    330 * Clear all patches of the given OPL3 instance
    331 */
    332void snd_opl3_clear_patches(struct snd_opl3 *opl3)
    333{
    334	int i;
    335	for (i = 0; i <  OPL3_PATCH_HASH_SIZE; i++) {
    336		struct fm_patch *patch, *next;
    337		for (patch = opl3->patch_table[i]; patch; patch = next) {
    338			next = patch->next;
    339			kfree(patch);
    340		}
    341	}
    342	memset(opl3->patch_table, 0, sizeof(opl3->patch_table));
    343}
    344#endif /* OPL3_SUPPORT_SYNTH */
    345
    346/* ------------------------------ */
    347
    348void snd_opl3_reset(struct snd_opl3 * opl3)
    349{
    350	unsigned short opl3_reg;
    351
    352	unsigned short reg_side;
    353	unsigned char voice_offset;
    354
    355	int max_voices, i;
    356
    357	max_voices = (opl3->hardware < OPL3_HW_OPL3) ?
    358		MAX_OPL2_VOICES : MAX_OPL3_VOICES;
    359
    360	for (i = 0; i < max_voices; i++) {
    361		/* Get register array side and offset of voice */
    362		if (i < MAX_OPL2_VOICES) {
    363			/* Left register block for voices 0 .. 8 */
    364			reg_side = OPL3_LEFT;
    365			voice_offset = i;
    366		} else {
    367			/* Right register block for voices 9 .. 17 */
    368			reg_side = OPL3_RIGHT;
    369			voice_offset = i - MAX_OPL2_VOICES;
    370		}
    371		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]);
    372		opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */
    373		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]);
    374		opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */
    375
    376		opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
    377		opl3->command(opl3, opl3_reg, 0x00);	/* Note off */
    378	}
    379
    380	opl3->max_voices = MAX_OPL2_VOICES;
    381	opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2;
    382
    383	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);
    384	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);	/* Melodic mode */
    385	opl3->rhythm = 0;
    386}
    387
    388EXPORT_SYMBOL(snd_opl3_reset);
    389
    390static int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note)
    391{
    392	unsigned short reg_side;
    393	unsigned char voice_offset;
    394
    395	unsigned short opl3_reg;
    396	unsigned char reg_val;
    397
    398	/* Voices 0 -  8 in OPL2 mode */
    399	/* Voices 0 - 17 in OPL3 mode */
    400	if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ?
    401			    MAX_OPL3_VOICES : MAX_OPL2_VOICES))
    402		return -EINVAL;
    403
    404	/* Get register array side and offset of voice */
    405	if (note->voice < MAX_OPL2_VOICES) {
    406		/* Left register block for voices 0 .. 8 */
    407		reg_side = OPL3_LEFT;
    408		voice_offset = note->voice;
    409	} else {
    410		/* Right register block for voices 9 .. 17 */
    411		reg_side = OPL3_RIGHT;
    412		voice_offset = note->voice - MAX_OPL2_VOICES;
    413	}
    414
    415	/* Set lower 8 bits of note frequency */
    416	reg_val = (unsigned char) note->fnum;
    417	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
    418	opl3->command(opl3, opl3_reg, reg_val);
    419	
    420	reg_val = 0x00;
    421	/* Set output sound flag */
    422	if (note->key_on)
    423		reg_val |= OPL3_KEYON_BIT;
    424	/* Set octave */
    425	reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK;
    426	/* Set higher 2 bits of note frequency */
    427	reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK;
    428
    429	/* Set OPL3 KEYON_BLOCK register of requested voice */ 
    430	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
    431	opl3->command(opl3, opl3_reg, reg_val);
    432
    433	return 0;
    434}
    435
    436
    437static int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * voice)
    438{
    439	unsigned short reg_side;
    440	unsigned char op_offset;
    441	unsigned char voice_offset, voice_op;
    442
    443	unsigned short opl3_reg;
    444	unsigned char reg_val;
    445
    446	/* Only operators 1 and 2 */
    447	if (voice->op > 1)
    448		return -EINVAL;
    449	/* Voices 0 -  8 in OPL2 mode */
    450	/* Voices 0 - 17 in OPL3 mode */
    451	if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ?
    452			     MAX_OPL3_VOICES : MAX_OPL2_VOICES))
    453		return -EINVAL;
    454
    455	/* Get register array side and offset of voice */
    456	if (voice->voice < MAX_OPL2_VOICES) {
    457		/* Left register block for voices 0 .. 8 */
    458		reg_side = OPL3_LEFT;
    459		voice_offset = voice->voice;
    460	} else {
    461		/* Right register block for voices 9 .. 17 */
    462		reg_side = OPL3_RIGHT;
    463		voice_offset = voice->voice - MAX_OPL2_VOICES;
    464	}
    465	/* Get register offset of operator */
    466	voice_offset = array_index_nospec(voice_offset, MAX_OPL2_VOICES);
    467	voice_op = array_index_nospec(voice->op, 4);
    468	op_offset = snd_opl3_regmap[voice_offset][voice_op];
    469
    470	reg_val = 0x00;
    471	/* Set amplitude modulation (tremolo) effect */
    472	if (voice->am)
    473		reg_val |= OPL3_TREMOLO_ON;
    474	/* Set vibrato effect */
    475	if (voice->vibrato)
    476		reg_val |= OPL3_VIBRATO_ON;
    477	/* Set sustaining sound phase */
    478	if (voice->do_sustain)
    479		reg_val |= OPL3_SUSTAIN_ON;
    480	/* Set keyboard scaling bit */ 
    481	if (voice->kbd_scale)
    482		reg_val |= OPL3_KSR;
    483	/* Set harmonic or frequency multiplier */
    484	reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK;
    485
    486	/* Set OPL3 AM_VIB register of requested voice/operator */ 
    487	opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset);
    488	opl3->command(opl3, opl3_reg, reg_val);
    489
    490	/* Set decreasing volume of higher notes */
    491	reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK;
    492	/* Set output volume */
    493	reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK;
    494
    495	/* Set OPL3 KSL_LEVEL register of requested voice/operator */ 
    496	opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset);
    497	opl3->command(opl3, opl3_reg, reg_val);
    498
    499	/* Set attack phase level */
    500	reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK;
    501	/* Set decay phase level */
    502	reg_val |= voice->decay & OPL3_DECAY_MASK;
    503
    504	/* Set OPL3 ATTACK_DECAY register of requested voice/operator */ 
    505	opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset);
    506	opl3->command(opl3, opl3_reg, reg_val);
    507
    508	/* Set sustain phase level */
    509	reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK;
    510	/* Set release phase level */
    511	reg_val |= voice->release & OPL3_RELEASE_MASK;
    512
    513	/* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ 
    514	opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
    515	opl3->command(opl3, opl3_reg, reg_val);
    516
    517	/* Set inter-operator feedback */
    518	reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK;
    519	/* Set inter-operator connection */
    520	if (voice->connection)
    521		reg_val |= OPL3_CONNECTION_BIT;
    522	/* OPL-3 only */
    523	if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) {
    524		if (voice->left)
    525			reg_val |= OPL3_VOICE_TO_LEFT;
    526		if (voice->right)
    527			reg_val |= OPL3_VOICE_TO_RIGHT;
    528	}
    529	/* Feedback/connection bits are applicable to voice */
    530	opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
    531	opl3->command(opl3, opl3_reg, reg_val);
    532
    533	/* Select waveform */
    534	reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK;
    535	opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset);
    536	opl3->command(opl3, opl3_reg, reg_val);
    537
    538	return 0;
    539}
    540
    541static int snd_opl3_set_params(struct snd_opl3 * opl3, struct snd_dm_fm_params * params)
    542{
    543	unsigned char reg_val;
    544
    545	reg_val = 0x00;
    546	/* Set keyboard split method */
    547	if (params->kbd_split)
    548		reg_val |= OPL3_KEYBOARD_SPLIT;
    549	opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val);
    550
    551	reg_val = 0x00;
    552	/* Set amplitude modulation (tremolo) depth */
    553	if (params->am_depth)
    554		reg_val |= OPL3_TREMOLO_DEPTH;
    555	/* Set vibrato depth */
    556	if (params->vib_depth)
    557		reg_val |= OPL3_VIBRATO_DEPTH;
    558	/* Set percussion mode */
    559	if (params->rhythm) {
    560		reg_val |= OPL3_PERCUSSION_ENABLE;
    561		opl3->rhythm = 1;
    562	} else {
    563		opl3->rhythm = 0;
    564	}
    565	/* Play percussion instruments */
    566	if (params->bass)
    567		reg_val |= OPL3_BASSDRUM_ON;
    568	if (params->snare)
    569		reg_val |= OPL3_SNAREDRUM_ON;
    570	if (params->tomtom)
    571		reg_val |= OPL3_TOMTOM_ON;
    572	if (params->cymbal)
    573		reg_val |= OPL3_CYMBAL_ON;
    574	if (params->hihat)
    575		reg_val |= OPL3_HIHAT_ON;
    576
    577	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val);
    578	return 0;
    579}
    580
    581static int snd_opl3_set_mode(struct snd_opl3 * opl3, int mode)
    582{
    583	if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3))
    584		return -EINVAL;
    585
    586	opl3->fm_mode = mode;
    587	if (opl3->hardware >= OPL3_HW_OPL3)
    588		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00);	/* Clear 4-op connections */
    589
    590	return 0;
    591}
    592
    593static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection)
    594{
    595	unsigned char reg_val;
    596
    597	/* OPL-3 only */
    598	if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3)
    599		return -EINVAL;
    600
    601	reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 |
    602				OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2);
    603	/* Set 4-op connections */
    604	opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val);
    605
    606	return 0;
    607}
    608