brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · 9f26f66 Raw
440 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Line 6 Linux USB driver4 *5 * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)6 */7 8#include <linux/slab.h>9#include <sound/core.h>10#include <sound/pcm.h>11#include <sound/pcm_params.h>12 13#include "capture.h"14#include "driver.h"15#include "pcm.h"16#include "playback.h"17 18/*19	Software stereo volume control.20*/21static void change_volume(struct urb *urb_out, int volume[],22			  int bytes_per_frame)23{24	int chn = 0;25 26	if (volume[0] == 256 && volume[1] == 256)27		return;		/* maximum volume - no change */28 29	if (bytes_per_frame == 4) {30		__le16 *p, *buf_end;31 32		p = (__le16 *)urb_out->transfer_buffer;33		buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);34 35		for (; p < buf_end; ++p) {36			short pv = le16_to_cpu(*p);37			int val = (pv * volume[chn & 1]) >> 8;38			pv = clamp(val, -0x8000, 0x7fff);39			*p = cpu_to_le16(pv);40			++chn;41		}42	} else if (bytes_per_frame == 6) {43		unsigned char *p, *buf_end;44 45		p = (unsigned char *)urb_out->transfer_buffer;46		buf_end = p + urb_out->transfer_buffer_length;47 48		for (; p < buf_end; p += 3) {49			int val;50 51			val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);52			val = (val * volume[chn & 1]) >> 8;53			val = clamp(val, -0x800000, 0x7fffff);54			p[0] = val;55			p[1] = val >> 8;56			p[2] = val >> 16;57			++chn;58		}59	}60}61 62/*63	Create signal for impulse response test.64*/65static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,66				       struct urb *urb_out, int bytes_per_frame)67{68	int frames = urb_out->transfer_buffer_length / bytes_per_frame;69 70	if (bytes_per_frame == 4) {71		int i;72		short *pi = (short *)line6pcm->prev_fbuf;73		short *po = (short *)urb_out->transfer_buffer;74 75		for (i = 0; i < frames; ++i) {76			po[0] = pi[0];77			po[1] = 0;78			pi += 2;79			po += 2;80		}81	} else if (bytes_per_frame == 6) {82		int i, j;83		unsigned char *pi = line6pcm->prev_fbuf;84		unsigned char *po = urb_out->transfer_buffer;85 86		for (i = 0; i < frames; ++i) {87			for (j = 0; j < bytes_per_frame / 2; ++j)88				po[j] = pi[j];89 90			for (; j < bytes_per_frame; ++j)91				po[j] = 0;92 93			pi += bytes_per_frame;94			po += bytes_per_frame;95		}96	}97	if (--line6pcm->impulse_count <= 0) {98		((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame -99							      1] =100		    line6pcm->impulse_volume;101		line6pcm->impulse_count = line6pcm->impulse_period;102	}103}104 105/*106	Add signal to buffer for software monitoring.107*/108static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,109			       int volume, int bytes_per_frame)110{111	if (volume == 0)112		return;		/* zero volume - no change */113 114	if (bytes_per_frame == 4) {115		__le16 *pi, *po, *buf_end;116 117		pi = (__le16 *)signal;118		po = (__le16 *)urb_out->transfer_buffer;119		buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);120 121		for (; po < buf_end; ++pi, ++po) {122			short pov = le16_to_cpu(*po);123			short piv = le16_to_cpu(*pi);124			int val = pov + ((piv * volume) >> 8);125			pov = clamp(val, -0x8000, 0x7fff);126			*po = cpu_to_le16(pov);127		}128	}129 130	/*131	   We don't need to handle devices with 6 bytes per frame here132	   since they all support hardware monitoring.133	 */134}135 136/*137	Find a free URB, prepare audio data, and submit URB.138	must be called in line6pcm->out.lock context139*/140static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)141{142	int index;143	int i, urb_size, urb_frames;144	int ret;145	const int bytes_per_frame =146		line6pcm->properties->bytes_per_channel *147		line6pcm->properties->playback_hw.channels_max;148	const int frame_increment =149		line6pcm->properties->rates.rats[0].num_min;150	const int frame_factor =151		line6pcm->properties->rates.rats[0].den *152		(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);153	struct urb *urb_out;154 155	index = find_first_zero_bit(&line6pcm->out.active_urbs,156				    line6pcm->line6->iso_buffers);157 158	if (index < 0 || index >= line6pcm->line6->iso_buffers) {159		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");160		return -EINVAL;161	}162 163	urb_out = line6pcm->out.urbs[index];164	urb_size = 0;165 166	/* TODO: this may not work for LINE6_ISO_PACKETS != 1 */167	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {168		/* compute frame size for given sampling rate */169		int fsize = 0;170		struct usb_iso_packet_descriptor *fout =171		    &urb_out->iso_frame_desc[i];172 173		fsize = line6pcm->prev_fsize;174		if (fsize == 0) {175			int n;176 177			line6pcm->out.count += frame_increment;178			n = line6pcm->out.count / frame_factor;179			line6pcm->out.count -= n * frame_factor;180			fsize = n;181		}182 183		fsize *= bytes_per_frame;184 185		fout->offset = urb_size;186		fout->length = fsize;187		urb_size += fsize;188	}189 190	if (urb_size == 0) {191		/* can't determine URB size */192		dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n");193		return -EINVAL;194	}195 196	urb_frames = urb_size / bytes_per_frame;197	urb_out->transfer_buffer =198	    line6pcm->out.buffer +199	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;200	urb_out->transfer_buffer_length = urb_size;201	urb_out->context = line6pcm;202 203	if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running) &&204	    !test_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags)) {205		struct snd_pcm_runtime *runtime =206		    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;207 208		if (line6pcm->out.pos + urb_frames > runtime->buffer_size) {209			/*210			   The transferred area goes over buffer boundary,211			   copy the data to the temp buffer.212			 */213			int len;214 215			len = runtime->buffer_size - line6pcm->out.pos;216 217			if (len > 0) {218				memcpy(urb_out->transfer_buffer,219				       runtime->dma_area +220				       line6pcm->out.pos * bytes_per_frame,221				       len * bytes_per_frame);222				memcpy(urb_out->transfer_buffer +223				       len * bytes_per_frame, runtime->dma_area,224				       (urb_frames - len) * bytes_per_frame);225			} else226				dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n",227					len);228		} else {229			memcpy(urb_out->transfer_buffer,230			       runtime->dma_area +231			       line6pcm->out.pos * bytes_per_frame,232			       urb_out->transfer_buffer_length);233		}234 235		line6pcm->out.pos += urb_frames;236		if (line6pcm->out.pos >= runtime->buffer_size)237			line6pcm->out.pos -= runtime->buffer_size;238 239		change_volume(urb_out, line6pcm->volume_playback,240			      bytes_per_frame);241	} else {242		memset(urb_out->transfer_buffer, 0,243		       urb_out->transfer_buffer_length);244	}245 246	spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING);247	if (line6pcm->prev_fbuf) {248		if (test_bit(LINE6_STREAM_IMPULSE, &line6pcm->out.running)) {249			create_impulse_test_signal(line6pcm, urb_out,250						   bytes_per_frame);251			if (test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) {252				line6_capture_copy(line6pcm,253						   urb_out->transfer_buffer,254						   urb_out->255						   transfer_buffer_length);256				line6_capture_check_period(line6pcm,257					urb_out->transfer_buffer_length);258			}259		} else {260			if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)261			    && line6pcm->out.running && line6pcm->in.running)262				add_monitor_signal(urb_out, line6pcm->prev_fbuf,263						   line6pcm->volume_monitor,264						   bytes_per_frame);265		}266		line6pcm->prev_fbuf = NULL;267		line6pcm->prev_fsize = 0;268	}269	spin_unlock(&line6pcm->in.lock);270 271	ret = usb_submit_urb(urb_out, GFP_ATOMIC);272 273	if (ret == 0)274		set_bit(index, &line6pcm->out.active_urbs);275	else276		dev_err(line6pcm->line6->ifcdev,277			"URB out #%d submission failed (%d)\n", index, ret);278 279	return 0;280}281 282/*283	Submit all currently available playback URBs.284	must be called in line6pcm->out.lock context285 */286int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)287{288	int ret = 0, i;289 290	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {291		ret = submit_audio_out_urb(line6pcm);292		if (ret < 0)293			break;294	}295 296	return ret;297}298 299/*300	Callback for completed playback URB.301*/302static void audio_out_callback(struct urb *urb)303{304	int i, index, length = 0, shutdown = 0;305	unsigned long flags;306	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;307	struct snd_pcm_substream *substream =308	    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);309	const int bytes_per_frame =310		line6pcm->properties->bytes_per_channel *311		line6pcm->properties->playback_hw.channels_max;312 313#if USE_CLEAR_BUFFER_WORKAROUND314	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);315#endif316 317	line6pcm->out.last_frame = urb->start_frame;318 319	/* find index of URB */320	for (index = 0; index < line6pcm->line6->iso_buffers; index++)321		if (urb == line6pcm->out.urbs[index])322			break;323 324	if (index >= line6pcm->line6->iso_buffers)325		return;		/* URB has been unlinked asynchronously */326 327	for (i = 0; i < LINE6_ISO_PACKETS; i++)328		length += urb->iso_frame_desc[i].length;329 330	spin_lock_irqsave(&line6pcm->out.lock, flags);331 332	if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {333		struct snd_pcm_runtime *runtime = substream->runtime;334 335		line6pcm->out.pos_done +=336		    length / bytes_per_frame;337 338		if (line6pcm->out.pos_done >= runtime->buffer_size)339			line6pcm->out.pos_done -= runtime->buffer_size;340	}341 342	clear_bit(index, &line6pcm->out.active_urbs);343 344	for (i = 0; i < LINE6_ISO_PACKETS; i++)345		if (urb->iso_frame_desc[i].status == -EXDEV) {346			shutdown = 1;347			break;348		}349 350	if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs))351		shutdown = 1;352 353	if (!shutdown) {354		submit_audio_out_urb(line6pcm);355 356		if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {357			line6pcm->out.bytes += length;358			if (line6pcm->out.bytes >= line6pcm->out.period) {359				line6pcm->out.bytes %= line6pcm->out.period;360				spin_unlock(&line6pcm->out.lock);361				snd_pcm_period_elapsed(substream);362				spin_lock(&line6pcm->out.lock);363			}364		}365	}366	spin_unlock_irqrestore(&line6pcm->out.lock, flags);367}368 369/* open playback callback */370static int snd_line6_playback_open(struct snd_pcm_substream *substream)371{372	int err;373	struct snd_pcm_runtime *runtime = substream->runtime;374	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);375 376	err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,377					    &line6pcm->properties->rates);378	if (err < 0)379		return err;380 381	runtime->hw = line6pcm->properties->playback_hw;382	return 0;383}384 385/* close playback callback */386static int snd_line6_playback_close(struct snd_pcm_substream *substream)387{388	return 0;389}390 391/* playback operators */392const struct snd_pcm_ops snd_line6_playback_ops = {393	.open = snd_line6_playback_open,394	.close = snd_line6_playback_close,395	.hw_params = snd_line6_hw_params,396	.hw_free = snd_line6_hw_free,397	.prepare = snd_line6_prepare,398	.trigger = snd_line6_trigger,399	.pointer = snd_line6_pointer,400};401 402int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)403{404	struct usb_line6 *line6 = line6pcm->line6;405	int i;406 407	line6pcm->out.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *),408				     GFP_KERNEL);409	if (line6pcm->out.urbs == NULL)410		return -ENOMEM;411 412	/* create audio URBs and fill in constant values: */413	for (i = 0; i < line6->iso_buffers; ++i) {414		struct urb *urb;415 416		/* URB for audio out: */417		urb = line6pcm->out.urbs[i] =418		    usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);419 420		if (urb == NULL)421			return -ENOMEM;422 423		urb->dev = line6->usbdev;424		urb->pipe =425		    usb_sndisocpipe(line6->usbdev,426				    line6->properties->ep_audio_w &427				    USB_ENDPOINT_NUMBER_MASK);428		urb->transfer_flags = URB_ISO_ASAP;429		urb->start_frame = -1;430		urb->number_of_packets = LINE6_ISO_PACKETS;431		urb->interval = LINE6_ISO_INTERVAL;432		urb->error_count = 0;433		urb->complete = audio_out_callback;434		if (usb_urb_ep_type_check(urb))435			return -EINVAL;436	}437 438	return 0;439}440