xref: /openbmc/linux/sound/drivers/opl3/opl3_drums.c (revision e3de2a40)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *   OPL2/OPL3/OPL4 FM routines for internal percussion channels
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include "opl3_voice.h"
91da177e4SLinus Torvalds 
10e3de2a40STakashi Iwai static const char snd_opl3_drum_table[47] =
111da177e4SLinus Torvalds {
121da177e4SLinus Torvalds 	OPL3_BASSDRUM_ON,  OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,	/* 35 - 37 */
131da177e4SLinus Torvalds 	OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON,     OPL3_SNAREDRUM_ON, /* 38 - 40 */
141da177e4SLinus Torvalds 	OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,     OPL3_BASSDRUM_ON,	/* 41 - 43 */
151da177e4SLinus Torvalds 	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,	/* 44 - 46 */
161da177e4SLinus Torvalds 	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,	/* 47 - 49 */
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 	OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 50 - 52 */
191da177e4SLinus Torvalds 	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 53 - 55 */
201da177e4SLinus Torvalds 	OPL3_HIHAT_ON,     OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,	/* 56 - 58 */
211da177e4SLinus Torvalds 	OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 59 - 61 */
221da177e4SLinus Torvalds 	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 62 - 64 */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 65 - 67 */
251da177e4SLinus Torvalds 	OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,     OPL3_HIHAT_ON,	/* 68 - 70 */
261da177e4SLinus Torvalds 	OPL3_HIHAT_ON,     OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,	/* 71 - 73 */
271da177e4SLinus Torvalds 	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 74 - 76 */
281da177e4SLinus Torvalds 	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 77 - 79 */
291da177e4SLinus Torvalds 	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON			/* 80 - 81 */
301da177e4SLinus Torvalds };
311da177e4SLinus Torvalds 
325b1646a8STakashi Iwai struct snd_opl3_drum_voice {
331da177e4SLinus Torvalds 	int voice;
341da177e4SLinus Torvalds 	int op;
351da177e4SLinus Torvalds 	unsigned char am_vib;
361da177e4SLinus Torvalds 	unsigned char ksl_level;
371da177e4SLinus Torvalds 	unsigned char attack_decay;
381da177e4SLinus Torvalds 	unsigned char sustain_release;
391da177e4SLinus Torvalds 	unsigned char feedback_connection;
401da177e4SLinus Torvalds 	unsigned char wave_select;
415b1646a8STakashi Iwai };
421da177e4SLinus Torvalds 
435b1646a8STakashi Iwai struct snd_opl3_drum_note {
441da177e4SLinus Torvalds 	int voice;
451da177e4SLinus Torvalds 	unsigned char fnum;
461da177e4SLinus Torvalds 	unsigned char octave_f;
471da177e4SLinus Torvalds 	unsigned char feedback_connection;
485b1646a8STakashi Iwai };
491da177e4SLinus Torvalds 
50fe27463aSTakashi Iwai static const struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
51fe27463aSTakashi Iwai static const struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
52fe27463aSTakashi Iwai static const struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
531da177e4SLinus Torvalds 
54fe27463aSTakashi Iwai static const struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
551da177e4SLinus Torvalds 
56fe27463aSTakashi Iwai static const struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
57fe27463aSTakashi Iwai static const struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
581da177e4SLinus Torvalds 
59fe27463aSTakashi Iwai static const struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
60fe27463aSTakashi Iwai static const struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
611da177e4SLinus Torvalds 
62fe27463aSTakashi Iwai static const struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /*
651da177e4SLinus Torvalds  * set drum voice characteristics
661da177e4SLinus Torvalds  */
snd_opl3_drum_voice_set(struct snd_opl3 * opl3,const struct snd_opl3_drum_voice * data)675b1646a8STakashi Iwai static void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
68fe27463aSTakashi Iwai 				    const struct snd_opl3_drum_voice *data)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
711da177e4SLinus Torvalds 	unsigned char voice_offset = data->voice;
721da177e4SLinus Torvalds 	unsigned short opl3_reg;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	/* Set OPL3 AM_VIB register */
751da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset);
761da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->am_vib);
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	/* Set OPL3 KSL_LEVEL register */
791da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
801da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->ksl_level);
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	/* Set OPL3 ATTACK_DECAY register */
831da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset);
841da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->attack_decay);
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	/* Set OPL3 SUSTAIN_RELEASE register */
871da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
881da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->sustain_release);
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	/* Set OPL3 FEEDBACK_CONNECTION register */
911da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
921da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->feedback_connection);
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	/* Select waveform */
951da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset);
961da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->wave_select);
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds /*
1001da177e4SLinus Torvalds  * Set drum voice pitch
1011da177e4SLinus Torvalds  */
snd_opl3_drum_note_set(struct snd_opl3 * opl3,const struct snd_opl3_drum_note * data)1025b1646a8STakashi Iwai static void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
103fe27463aSTakashi Iwai 				   const struct snd_opl3_drum_note *data)
1041da177e4SLinus Torvalds {
1051da177e4SLinus Torvalds 	unsigned char voice_offset = data->voice;
1061da177e4SLinus Torvalds 	unsigned short opl3_reg;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 	/* Set OPL3 FNUM_LOW register */
1091da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset);
1101da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->fnum);
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	/* Set OPL3 KEYON_BLOCK register */
1131da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset);
1141da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, data->octave_f);
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds /*
1181da177e4SLinus Torvalds  * Set drum voice volume and position
1191da177e4SLinus Torvalds  */
snd_opl3_drum_vol_set(struct snd_opl3 * opl3,const struct snd_opl3_drum_voice * data,int vel,struct snd_midi_channel * chan)1205b1646a8STakashi Iwai static void snd_opl3_drum_vol_set(struct snd_opl3 *opl3,
121fe27463aSTakashi Iwai 				  const struct snd_opl3_drum_voice *data,
1225b1646a8STakashi Iwai 				  int vel, struct snd_midi_channel *chan)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
1251da177e4SLinus Torvalds 	unsigned char voice_offset = data->voice;
1261da177e4SLinus Torvalds 	unsigned char reg_val;
1271da177e4SLinus Torvalds 	unsigned short opl3_reg;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	/* Set OPL3 KSL_LEVEL register */
1301da177e4SLinus Torvalds 	reg_val = data->ksl_level;
1311da177e4SLinus Torvalds 	snd_opl3_calc_volume(&reg_val, vel, chan);
1321da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
1331da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, reg_val);
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	/* Set OPL3 FEEDBACK_CONNECTION register */
1361da177e4SLinus Torvalds 	/* Set output voice connection */
1371da177e4SLinus Torvalds 	reg_val = data->feedback_connection | OPL3_STEREO_BITS;
1381da177e4SLinus Torvalds 	if (chan->gm_pan < 43)
1391da177e4SLinus Torvalds 		reg_val &= ~OPL3_VOICE_TO_RIGHT;
1401da177e4SLinus Torvalds 	if (chan->gm_pan > 85)
1411da177e4SLinus Torvalds 		reg_val &= ~OPL3_VOICE_TO_LEFT;
1421da177e4SLinus Torvalds 	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
1431da177e4SLinus Torvalds 	opl3->command(opl3, opl3_reg, reg_val);
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds /*
1471da177e4SLinus Torvalds  * Loads drum voices at init time
1481da177e4SLinus Torvalds  */
snd_opl3_load_drums(struct snd_opl3 * opl3)1495b1646a8STakashi Iwai void snd_opl3_load_drums(struct snd_opl3 *opl3)
1501da177e4SLinus Torvalds {
1511da177e4SLinus Torvalds 	snd_opl3_drum_voice_set(opl3, &bass_op0);
1521da177e4SLinus Torvalds 	snd_opl3_drum_voice_set(opl3, &bass_op1);
1531da177e4SLinus Torvalds 	snd_opl3_drum_note_set(opl3, &bass_note);
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	snd_opl3_drum_voice_set(opl3, &hihat);
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	snd_opl3_drum_voice_set(opl3, &snare);
1581da177e4SLinus Torvalds 	snd_opl3_drum_note_set(opl3, &snare_note);
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	snd_opl3_drum_voice_set(opl3, &tomtom);
1611da177e4SLinus Torvalds 	snd_opl3_drum_note_set(opl3, &tomtom_note);
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	snd_opl3_drum_voice_set(opl3, &cymbal);
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds /*
1671da177e4SLinus Torvalds  * Switch drum voice on or off
1681da177e4SLinus Torvalds  */
snd_opl3_drum_switch(struct snd_opl3 * opl3,int note,int vel,int on_off,struct snd_midi_channel * chan)1695b1646a8STakashi Iwai void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off,
1705b1646a8STakashi Iwai 			  struct snd_midi_channel *chan)
1711da177e4SLinus Torvalds {
1721da177e4SLinus Torvalds 	unsigned char drum_mask;
173fe27463aSTakashi Iwai 	const struct snd_opl3_drum_voice *drum_voice;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
1761da177e4SLinus Torvalds 		return;
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	if ((note < 35) || (note > 81))
1791da177e4SLinus Torvalds 		return;
1801da177e4SLinus Torvalds 	drum_mask = snd_opl3_drum_table[note - 35];
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	if (on_off) {
1831da177e4SLinus Torvalds 		switch (drum_mask) {
1841da177e4SLinus Torvalds 		case OPL3_BASSDRUM_ON:
1851da177e4SLinus Torvalds 			drum_voice = &bass_op1;
1861da177e4SLinus Torvalds 			break;
1871da177e4SLinus Torvalds 		case OPL3_HIHAT_ON:
1881da177e4SLinus Torvalds 			drum_voice = &hihat;
1891da177e4SLinus Torvalds 			break;
1901da177e4SLinus Torvalds 		case OPL3_SNAREDRUM_ON:
1911da177e4SLinus Torvalds 			drum_voice = &snare;
1921da177e4SLinus Torvalds 			break;
1931da177e4SLinus Torvalds 		case OPL3_TOMTOM_ON:
1941da177e4SLinus Torvalds 			drum_voice = &tomtom;
1951da177e4SLinus Torvalds 			break;
1961da177e4SLinus Torvalds 		case OPL3_CYMBAL_ON:
1971da177e4SLinus Torvalds 			drum_voice = &cymbal;
1981da177e4SLinus Torvalds 			break;
1991da177e4SLinus Torvalds 		default:
2001da177e4SLinus Torvalds 			drum_voice = &tomtom;
2011da177e4SLinus Torvalds 		}
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 		snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan);
2041da177e4SLinus Torvalds 		opl3->drum_reg |= drum_mask;
2051da177e4SLinus Torvalds 	} else {
2061da177e4SLinus Torvalds 		opl3->drum_reg &= ~drum_mask;
2071da177e4SLinus Torvalds 	}
2081da177e4SLinus Torvalds 	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
2091da177e4SLinus Torvalds 			 opl3->drum_reg);
2101da177e4SLinus Torvalds }
211