brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · 8e3318e Raw
615 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>4 *  Routines for control of MPU-401 in UART mode5 *6 *  MPU-401 supports UART mode which is not capable generate transmit7 *  interrupts thus output is done via polling. Without interrupt,8 *  input is done also via polling. Do not expect good performance.9 *10 *   13-03-2003:11 *      Added support for different kind of hardware I/O. Build in choices12 *      are port and mmio. For other kind of I/O, set mpu->read and13 *      mpu->write to your own I/O functions.14 */15 16#include <linux/io.h>17#include <linux/delay.h>18#include <linux/init.h>19#include <linux/slab.h>20#include <linux/ioport.h>21#include <linux/module.h>22#include <linux/interrupt.h>23#include <linux/errno.h>24#include <sound/core.h>25#include <sound/mpu401.h>26 27MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");28MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode");29MODULE_LICENSE("GPL");30 31static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu);32static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu);33 34/*35 36 */37 38#define snd_mpu401_input_avail(mpu) \39	(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY))40#define snd_mpu401_output_ready(mpu) \41	(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL))42 43/* Build in lowlevel io */44static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,45			      unsigned long addr)46{47	outb(data, addr);48}49 50static unsigned char mpu401_read_port(struct snd_mpu401 *mpu,51				      unsigned long addr)52{53	return inb(addr);54}55 56static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data,57			      unsigned long addr)58{59	writeb(data, (void __iomem *)addr);60}61 62static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu,63				      unsigned long addr)64{65	return readb((void __iomem *)addr);66}67/*  */68 69static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)70{71	int timeout = 100000;72	for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--)73		mpu->read(mpu, MPU401D(mpu));74#ifdef CONFIG_SND_DEBUG75	if (timeout <= 0)76		dev_err(mpu->rmidi->dev,77			"cmd: clear rx timeout (status = 0x%x)\n",78			mpu->read(mpu, MPU401C(mpu)));79#endif80}81 82static void uart_interrupt_tx(struct snd_mpu401 *mpu)83{84	unsigned long flags;85 86	if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&87	    test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {88		spin_lock_irqsave(&mpu->output_lock, flags);89		snd_mpu401_uart_output_write(mpu);90		spin_unlock_irqrestore(&mpu->output_lock, flags);91	}92}93 94static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)95{96	unsigned long flags;97 98	if (mpu->info_flags & MPU401_INFO_INPUT) {99		spin_lock_irqsave(&mpu->input_lock, flags);100		if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))101			snd_mpu401_uart_input_read(mpu);102		else103			snd_mpu401_uart_clear_rx(mpu);104		spin_unlock_irqrestore(&mpu->input_lock, flags);105	}106	if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))107		/* ok. for better Tx performance try do some output108		   when input is done */109		uart_interrupt_tx(mpu);110}111 112/**113 * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler114 * @irq: the irq number115 * @dev_id: mpu401 instance116 *117 * Processes the interrupt for MPU401-UART i/o.118 *119 * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.120 */121irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)122{123	struct snd_mpu401 *mpu = dev_id;124	125	if (!mpu)126		return IRQ_NONE;127	_snd_mpu401_uart_interrupt(mpu);128	return IRQ_HANDLED;129}130 131EXPORT_SYMBOL(snd_mpu401_uart_interrupt);132 133/**134 * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler135 * @irq: the irq number136 * @dev_id: mpu401 instance137 *138 * Processes the interrupt for MPU401-UART output.139 *140 * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.141 */142irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)143{144	struct snd_mpu401 *mpu = dev_id;145	146	if (!mpu)147		return IRQ_NONE;148	uart_interrupt_tx(mpu);149	return IRQ_HANDLED;150}151 152EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);153 154/*155 * timer callback156 * reprogram the timer and call the interrupt job157 */158static void snd_mpu401_uart_timer(struct timer_list *t)159{160	struct snd_mpu401 *mpu = from_timer(mpu, t, timer);161	unsigned long flags;162 163	spin_lock_irqsave(&mpu->timer_lock, flags);164	/*mpu->mode |= MPU401_MODE_TIMER;*/165	mod_timer(&mpu->timer,  1 + jiffies);166	spin_unlock_irqrestore(&mpu->timer_lock, flags);167	if (mpu->rmidi)168		_snd_mpu401_uart_interrupt(mpu);169}170 171/*172 * initialize the timer callback if not programmed yet173 */174static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)175{176	unsigned long flags;177 178	spin_lock_irqsave (&mpu->timer_lock, flags);179	if (mpu->timer_invoked == 0) {180		timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);181		mod_timer(&mpu->timer, 1 + jiffies);182	} 183	mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :184		MPU401_MODE_OUTPUT_TIMER;185	spin_unlock_irqrestore (&mpu->timer_lock, flags);186}187 188/*189 * remove the timer callback if still active190 */191static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)192{193	unsigned long flags;194 195	spin_lock_irqsave (&mpu->timer_lock, flags);196	if (mpu->timer_invoked) {197		mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :198			~MPU401_MODE_OUTPUT_TIMER;199		if (! mpu->timer_invoked)200			del_timer(&mpu->timer);201	}202	spin_unlock_irqrestore (&mpu->timer_lock, flags);203}204 205/*206 * send a UART command207 * return zero if successful, non-zero for some errors208 */209 210static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,211			       int ack)212{213	unsigned long flags;214	int timeout, ok;215 216	spin_lock_irqsave(&mpu->input_lock, flags);217	if (mpu->hardware != MPU401_HW_TRID4DWAVE) {218		mpu->write(mpu, 0x00, MPU401D(mpu));219		/*snd_mpu401_uart_clear_rx(mpu);*/220	}221	/* ok. standard MPU-401 initialization */222	if (mpu->hardware != MPU401_HW_SB) {223		for (timeout = 1000; timeout > 0 &&224			     !snd_mpu401_output_ready(mpu); timeout--)225			udelay(10);226#ifdef CONFIG_SND_DEBUG227		if (!timeout)228			dev_err(mpu->rmidi->dev,229				"cmd: tx timeout (status = 0x%x)\n",230				mpu->read(mpu, MPU401C(mpu)));231#endif232	}233	mpu->write(mpu, cmd, MPU401C(mpu));234	if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) {235		ok = 0;236		timeout = 10000;237		while (!ok && timeout-- > 0) {238			if (snd_mpu401_input_avail(mpu)) {239				if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)240					ok = 1;241			}242		}243		if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)244			ok = 1;245	} else246		ok = 1;247	spin_unlock_irqrestore(&mpu->input_lock, flags);248	if (!ok) {249		dev_err(mpu->rmidi->dev,250			"cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n",251			cmd, mpu->port,252			mpu->read(mpu, MPU401C(mpu)),253			mpu->read(mpu, MPU401D(mpu)));254		return 1;255	}256	return 0;257}258 259static int snd_mpu401_do_reset(struct snd_mpu401 *mpu)260{261	if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1))262		return -EIO;263	if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0))264		return -EIO;265	return 0;266}267 268/*269 * input/output open/close - protected by open_mutex in rawmidi.c270 */271static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)272{273	struct snd_mpu401 *mpu;274	int err;275 276	mpu = substream->rmidi->private_data;277	if (mpu->open_input) {278		err = mpu->open_input(mpu);279		if (err < 0)280			return err;281	}282	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {283		if (snd_mpu401_do_reset(mpu) < 0)284			goto error_out;285	}286	mpu->substream_input = substream;287	set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);288	return 0;289 290error_out:291	if (mpu->open_input && mpu->close_input)292		mpu->close_input(mpu);293	return -EIO;294}295 296static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)297{298	struct snd_mpu401 *mpu;299	int err;300 301	mpu = substream->rmidi->private_data;302	if (mpu->open_output) {303		err = mpu->open_output(mpu);304		if (err < 0)305			return err;306	}307	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {308		if (snd_mpu401_do_reset(mpu) < 0)309			goto error_out;310	}311	mpu->substream_output = substream;312	set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);313	return 0;314 315error_out:316	if (mpu->open_output && mpu->close_output)317		mpu->close_output(mpu);318	return -EIO;319}320 321static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream)322{323	struct snd_mpu401 *mpu;324	int err = 0;325 326	mpu = substream->rmidi->private_data;327	clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);328	mpu->substream_input = NULL;329	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode))330		err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);331	if (mpu->close_input)332		mpu->close_input(mpu);333	if (err)334		return -EIO;335	return 0;336}337 338static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)339{340	struct snd_mpu401 *mpu;341	int err = 0;342 343	mpu = substream->rmidi->private_data;344	clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);345	mpu->substream_output = NULL;346	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))347		err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);348	if (mpu->close_output)349		mpu->close_output(mpu);350	if (err)351		return -EIO;352	return 0;353}354 355/*356 * trigger input callback357 */358static void359snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)360{361	unsigned long flags;362	struct snd_mpu401 *mpu;363	int max = 64;364 365	mpu = substream->rmidi->private_data;366	if (up) {367		if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER,368				       &mpu->mode)) {369			/* first time - flush FIFO */370			while (max-- > 0)371				mpu->read(mpu, MPU401D(mpu));372			if (mpu->info_flags & MPU401_INFO_USE_TIMER)373				snd_mpu401_uart_add_timer(mpu, 1);374		}375		376		/* read data in advance */377		spin_lock_irqsave(&mpu->input_lock, flags);378		snd_mpu401_uart_input_read(mpu);379		spin_unlock_irqrestore(&mpu->input_lock, flags);380	} else {381		if (mpu->info_flags & MPU401_INFO_USE_TIMER)382			snd_mpu401_uart_remove_timer(mpu, 1);383		clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);384	}385 386}387 388/*389 * transfer input pending data390 * call with input_lock spinlock held391 */392static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)393{394	int max = 128;395	unsigned char byte;396 397	while (max-- > 0) {398		if (! snd_mpu401_input_avail(mpu))399			break; /* input not available */400		byte = mpu->read(mpu, MPU401D(mpu));401		if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))402			snd_rawmidi_receive(mpu->substream_input, &byte, 1);403	}404}405 406/*407 *  Tx FIFO sizes:408 *    CS4237B			- 16 bytes409 *    AudioDrive ES1688         - 12 bytes410 *    S3 SonicVibes             -  8 bytes411 *    SoundBlaster AWE 64       -  2 bytes (ugly hardware)412 */413 414/*415 * write output pending bytes416 * call with output_lock spinlock held417 */418static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)419{420	unsigned char byte;421	int max = 256;422 423	do {424		if (snd_rawmidi_transmit_peek(mpu->substream_output,425					      &byte, 1) == 1) {426			/*427			 * Try twice because there is hardware that insists on428			 * setting the output busy bit after each write.429			 */430			if (!snd_mpu401_output_ready(mpu) &&431			    !snd_mpu401_output_ready(mpu))432				break;	/* Tx FIFO full - try again later */433			mpu->write(mpu, byte, MPU401D(mpu));434			snd_rawmidi_transmit_ack(mpu->substream_output, 1);435		} else {436			snd_mpu401_uart_remove_timer (mpu, 0);437			break;	/* no other data - leave the tx loop */438		}439	} while (--max > 0);440}441 442/*443 * output trigger callback444 */445static void446snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)447{448	unsigned long flags;449	struct snd_mpu401 *mpu;450 451	mpu = substream->rmidi->private_data;452	if (up) {453		set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);454 455		/* try to add the timer at each output trigger,456		 * since the output timer might have been removed in457		 * snd_mpu401_uart_output_write().458		 */459		if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))460			snd_mpu401_uart_add_timer(mpu, 0);461 462		/* output pending data */463		spin_lock_irqsave(&mpu->output_lock, flags);464		snd_mpu401_uart_output_write(mpu);465		spin_unlock_irqrestore(&mpu->output_lock, flags);466	} else {467		if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))468			snd_mpu401_uart_remove_timer(mpu, 0);469		clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);470	}471}472 473/*474 475 */476 477static const struct snd_rawmidi_ops snd_mpu401_uart_output =478{479	.open =		snd_mpu401_uart_output_open,480	.close =	snd_mpu401_uart_output_close,481	.trigger =	snd_mpu401_uart_output_trigger,482};483 484static const struct snd_rawmidi_ops snd_mpu401_uart_input =485{486	.open =		snd_mpu401_uart_input_open,487	.close =	snd_mpu401_uart_input_close,488	.trigger =	snd_mpu401_uart_input_trigger,489};490 491static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)492{493	struct snd_mpu401 *mpu = rmidi->private_data;494	if (mpu->irq >= 0)495		free_irq(mpu->irq, (void *) mpu);496	release_and_free_resource(mpu->res);497	kfree(mpu);498}499 500/**501 * snd_mpu401_uart_new - create an MPU401-UART instance502 * @card: the card instance503 * @device: the device index, zero-based504 * @hardware: the hardware type, MPU401_HW_XXXX505 * @port: the base address of MPU401 port506 * @info_flags: bitflags MPU401_INFO_XXX507 * @irq: the ISA irq number, -1 if not to be allocated508 * @rrawmidi: the pointer to store the new rawmidi instance509 *510 * Creates a new MPU-401 instance.511 *512 * Note that the rawmidi instance is returned on the rrawmidi argument,513 * not the mpu401 instance itself.  To access to the mpu401 instance,514 * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).515 *516 * Return: Zero if successful, or a negative error code.517 */518int snd_mpu401_uart_new(struct snd_card *card, int device,519			unsigned short hardware,520			unsigned long port,521			unsigned int info_flags,522			int irq,523			struct snd_rawmidi ** rrawmidi)524{525	struct snd_mpu401 *mpu;526	struct snd_rawmidi *rmidi;527	int in_enable, out_enable;528	int err;529 530	if (rrawmidi)531		*rrawmidi = NULL;532	if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT)))533		info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;534	in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;535	out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;536	err = snd_rawmidi_new(card, "MPU-401U", device,537			      out_enable, in_enable, &rmidi);538	if (err < 0)539		return err;540	mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);541	if (!mpu) {542		err = -ENOMEM;543		goto free_device;544	}545	rmidi->private_data = mpu;546	rmidi->private_free = snd_mpu401_uart_free;547	spin_lock_init(&mpu->input_lock);548	spin_lock_init(&mpu->output_lock);549	spin_lock_init(&mpu->timer_lock);550	mpu->hardware = hardware;551	mpu->irq = -1;552	mpu->rmidi = rmidi;553	if (! (info_flags & MPU401_INFO_INTEGRATED)) {554		int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;555		mpu->res = request_region(port, res_size, "MPU401 UART");556		if (!mpu->res) {557			dev_err(rmidi->dev,558				"mpu401_uart: unable to grab port 0x%lx size %d\n",559				port, res_size);560			err = -EBUSY;561			goto free_device;562		}563	}564	if (info_flags & MPU401_INFO_MMIO) {565		mpu->write = mpu401_write_mmio;566		mpu->read = mpu401_read_mmio;567	} else {568		mpu->write = mpu401_write_port;569		mpu->read = mpu401_read_port;570	}571	mpu->port = port;572	if (hardware == MPU401_HW_PC98II)573		mpu->cport = port + 2;574	else575		mpu->cport = port + 1;576	if (irq >= 0) {577		if (request_irq(irq, snd_mpu401_uart_interrupt, 0,578				"MPU401 UART", (void *) mpu)) {579			dev_err(rmidi->dev,580				"mpu401_uart: unable to grab IRQ %d\n", irq);581			err = -EBUSY;582			goto free_device;583		}584	}585	if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK))586		info_flags |= MPU401_INFO_USE_TIMER;587	mpu->info_flags = info_flags;588	mpu->irq = irq;589	if (card->shortname[0])590		snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",591			 card->shortname);592	else593		sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device);594	if (out_enable) {595		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,596				    &snd_mpu401_uart_output);597		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;598	}599	if (in_enable) {600		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,601				    &snd_mpu401_uart_input);602		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;603		if (out_enable)604			rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;605	}606	if (rrawmidi)607		*rrawmidi = rmidi;608	return 0;609free_device:610	snd_device_free(card, rmidi);611	return err;612}613 614EXPORT_SYMBOL(snd_mpu401_uart_new);615