File Index
Files and directories
Sign in
FILE

opl4_seq.c

File preview with syntax highlighting.

linux-6.19.14/sound/drivers/opl4/opl4_seq.c C
  1/*
  2 * OPL4 sequencer functions
  3 *
  4 * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
  5 * All rights reserved.
  6 *
  7 * Redistribution and use in source and binary forms, with or without
  8 * modification, are permitted provided that the following conditions
  9 * are met:
 10 * 1. Redistributions of source code must retain the above copyright
 11 *    notice, this list of conditions, and the following disclaimer,
 12 *    without modification.
 13 * 2. The name of the author may not be used to endorse or promote products
 14 *    derived from this software without specific prior written permission.
 15 *
 16 * Alternatively, this software may be distributed and/or modified under the
 17 * terms of the GNU General Public License as published by the Free Software
 18 * Foundation; either version 2 of the License, or (at your option) any later
 19 * version.
 20 *
 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 31 * SUCH DAMAGE.
 32 */
 33
 34#include "opl4_local.h"
 35#include <linux/init.h>
 36#include <linux/moduleparam.h>
 37#include <linux/module.h>
 38#include <sound/initval.h>
 39
 40MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 41MODULE_DESCRIPTION("OPL4 wavetable synth driver");
 42MODULE_LICENSE("Dual BSD/GPL");
 43
 44int volume_boost = 8;
 45
 46module_param(volume_boost, int, 0644);
 47MODULE_PARM_DESC(volume_boost, "Additional volume for OPL4 wavetable sounds.");
 48
 49static int snd_opl4_seq_use_inc(struct snd_opl4 *opl4)
 50{
 51	if (!try_module_get(opl4->card->module))
 52		return -EFAULT;
 53	return 0;
 54}
 55
 56static void snd_opl4_seq_use_dec(struct snd_opl4 *opl4)
 57{
 58	module_put(opl4->card->module);
 59}
 60
 61static int snd_opl4_seq_use(void *private_data, struct snd_seq_port_subscribe *info)
 62{
 63	struct snd_opl4 *opl4 = private_data;
 64	int err;
 65
 66	scoped_guard(mutex, &opl4->access_mutex) {
 67		if (opl4->used)
 68			return -EBUSY;
 69		opl4->used++;
 70
 71		if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
 72			err = snd_opl4_seq_use_inc(opl4);
 73			if (err < 0)
 74				return err;
 75		}
 76	}
 77
 78	snd_opl4_synth_reset(opl4);
 79	return 0;
 80}
 81
 82static int snd_opl4_seq_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 83{
 84	struct snd_opl4 *opl4 = private_data;
 85
 86	snd_opl4_synth_shutdown(opl4);
 87
 88	scoped_guard(mutex, &opl4->access_mutex) {
 89		opl4->used--;
 90	}
 91
 92	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
 93		snd_opl4_seq_use_dec(opl4);
 94	return 0;
 95}
 96
 97static const struct snd_midi_op opl4_ops = {
 98	.note_on =		snd_opl4_note_on,
 99	.note_off =		snd_opl4_note_off,
100	.note_terminate =	snd_opl4_terminate_note,
101	.control =		snd_opl4_control,
102	.sysex =		snd_opl4_sysex,
103};
104
105static int snd_opl4_seq_event_input(struct snd_seq_event *ev, int direct,
106				    void *private_data, int atomic, int hop)
107{
108	struct snd_opl4 *opl4 = private_data;
109
110	snd_midi_process_event(&opl4_ops, ev, opl4->chset);
111	return 0;
112}
113
114static void snd_opl4_seq_free_port(void *private_data)
115{
116	struct snd_opl4 *opl4 = private_data;
117
118	snd_midi_channel_free_set(opl4->chset);
119}
120
121static int snd_opl4_seq_probe(struct device *_dev)
122{
123	struct snd_seq_device *dev = to_seq_dev(_dev);
124	struct snd_opl4 *opl4;
125	int client;
126	struct snd_seq_port_callback pcallbacks;
127
128	opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
129	if (!opl4)
130		return -EINVAL;
131
132	if (snd_yrw801_detect(opl4) < 0)
133		return -ENODEV;
134
135	opl4->chset = snd_midi_channel_alloc_set(16);
136	if (!opl4->chset)
137		return -ENOMEM;
138	opl4->chset->private_data = opl4;
139
140	/* allocate new client */
141	client = snd_seq_create_kernel_client(opl4->card, opl4->seq_dev_num,
142					      "OPL4 Wavetable");
143	if (client < 0) {
144		snd_midi_channel_free_set(opl4->chset);
145		return client;
146	}
147	opl4->seq_client = client;
148	opl4->chset->client = client;
149
150	/* create new port */
151	memset(&pcallbacks, 0, sizeof(pcallbacks));
152	pcallbacks.owner = THIS_MODULE;
153	pcallbacks.use = snd_opl4_seq_use;
154	pcallbacks.unuse = snd_opl4_seq_unuse;
155	pcallbacks.event_input = snd_opl4_seq_event_input;
156	pcallbacks.private_free = snd_opl4_seq_free_port;
157	pcallbacks.private_data = opl4;
158
159	opl4->chset->port = snd_seq_event_port_attach(client, &pcallbacks,
160						      SNDRV_SEQ_PORT_CAP_WRITE |
161						      SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
162						      SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
163						      SNDRV_SEQ_PORT_TYPE_MIDI_GM |
164						      SNDRV_SEQ_PORT_TYPE_HARDWARE |
165						      SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
166						      16, 24,
167						      "OPL4 Wavetable Port");
168	if (opl4->chset->port < 0) {
169		int err = opl4->chset->port;
170		snd_midi_channel_free_set(opl4->chset);
171		snd_seq_delete_kernel_client(client);
172		opl4->seq_client = -1;
173		return err;
174	}
175	return 0;
176}
177
178static int snd_opl4_seq_remove(struct device *_dev)
179{
180	struct snd_seq_device *dev = to_seq_dev(_dev);
181	struct snd_opl4 *opl4;
182
183	opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
184	if (!opl4)
185		return -EINVAL;
186
187	if (opl4->seq_client >= 0) {
188		snd_seq_delete_kernel_client(opl4->seq_client);
189		opl4->seq_client = -1;
190	}
191	return 0;
192}
193
194static struct snd_seq_driver opl4_seq_driver = {
195	.driver = {
196		.name = KBUILD_MODNAME,
197		.probe = snd_opl4_seq_probe,
198		.remove = snd_opl4_seq_remove,
199	},
200	.id = SNDRV_SEQ_DEV_ID_OPL4,
201	.argsize = sizeof(struct snd_opl4 *),
202};
203
204module_snd_seq_driver(opl4_seq_driver);
~/ Read-only file preview.