brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 8f346d7 Raw
277 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  ALSA PCM device for the4 *  ALSA interface to ivtv PCM capture streams5 *6 *  Copyright (C) 2009,2012  Andy Walls <awalls@md.metrocast.net>7 *  Copyright (C) 2009  Devin Heitmueller <dheitmueller@kernellabs.com>8 *9 *  Portions of this work were sponsored by ONELAN Limited for the cx18 driver10 */11 12#include "ivtv-driver.h"13#include "ivtv-queue.h"14#include "ivtv-streams.h"15#include "ivtv-fileops.h"16#include "ivtv-alsa.h"17#include "ivtv-alsa-pcm.h"18 19#include <sound/core.h>20#include <sound/pcm.h>21 22 23static unsigned int pcm_debug;24module_param(pcm_debug, int, 0644);25MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");26 27#define dprintk(fmt, arg...) \28	do { \29		if (pcm_debug) \30			pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \31	} while (0)32 33static const struct snd_pcm_hardware snd_ivtv_hw_capture = {34	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |35		SNDRV_PCM_INFO_MMAP           |36		SNDRV_PCM_INFO_INTERLEAVED    |37		SNDRV_PCM_INFO_MMAP_VALID,38 39	.formats = SNDRV_PCM_FMTBIT_S16_LE,40 41	.rates = SNDRV_PCM_RATE_48000,42 43	.rate_min = 48000,44	.rate_max = 48000,45	.channels_min = 2,46	.channels_max = 2,47	.buffer_bytes_max = 62720 * 8,	/* just about the value in usbaudio.c */48	.period_bytes_min = 64,		/* 12544/2, */49	.period_bytes_max = 12544,50	.periods_min = 2,51	.periods_max = 98,		/* 12544, */52};53 54static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc,55					u8 *pcm_data,56					size_t num_bytes)57{58	struct snd_pcm_substream *substream;59	struct snd_pcm_runtime *runtime;60	unsigned int oldptr;61	unsigned int stride;62	int period_elapsed = 0;63	int length;64 65	dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zu\n", itvsc,66		pcm_data, num_bytes);67 68	substream = itvsc->capture_pcm_substream;69	if (substream == NULL) {70		dprintk("substream was NULL\n");71		return;72	}73 74	runtime = substream->runtime;75	if (runtime == NULL) {76		dprintk("runtime was NULL\n");77		return;78	}79 80	stride = runtime->frame_bits >> 3;81	if (stride == 0) {82		dprintk("stride is zero\n");83		return;84	}85 86	length = num_bytes / stride;87	if (length == 0) {88		dprintk("%s: length was zero\n", __func__);89		return;90	}91 92	if (runtime->dma_area == NULL) {93		dprintk("dma area was NULL - ignoring\n");94		return;95	}96 97	oldptr = itvsc->hwptr_done_capture;98	if (oldptr + length >= runtime->buffer_size) {99		unsigned int cnt =100			runtime->buffer_size - oldptr;101		memcpy(runtime->dma_area + oldptr * stride, pcm_data,102		       cnt * stride);103		memcpy(runtime->dma_area, pcm_data + cnt * stride,104		       length * stride - cnt * stride);105	} else {106		memcpy(runtime->dma_area + oldptr * stride, pcm_data,107		       length * stride);108	}109	snd_pcm_stream_lock(substream);110 111	itvsc->hwptr_done_capture += length;112	if (itvsc->hwptr_done_capture >=113	    runtime->buffer_size)114		itvsc->hwptr_done_capture -=115			runtime->buffer_size;116 117	itvsc->capture_transfer_done += length;118	if (itvsc->capture_transfer_done >=119	    runtime->period_size) {120		itvsc->capture_transfer_done -=121			runtime->period_size;122		period_elapsed = 1;123	}124 125	snd_pcm_stream_unlock(substream);126 127	if (period_elapsed)128		snd_pcm_period_elapsed(substream);129}130 131static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream)132{133	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);134	struct snd_pcm_runtime *runtime = substream->runtime;135	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;136	struct ivtv *itv = to_ivtv(v4l2_dev);137	struct ivtv_stream *s;138	struct ivtv_open_id item;139	int ret;140 141	/* Instruct the CX2341[56] to start sending packets */142	snd_ivtv_lock(itvsc);143 144	if (ivtv_init_on_first_open(itv)) {145		snd_ivtv_unlock(itvsc);146		return -ENXIO;147	}148 149	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];150 151	v4l2_fh_init(&item.fh, &s->vdev);152	item.itv = itv;153	item.type = s->type;154 155	/* See if the stream is available */156	if (ivtv_claim_stream(&item, item.type)) {157		/* No, it's already in use */158		v4l2_fh_exit(&item.fh);159		snd_ivtv_unlock(itvsc);160		return -EBUSY;161	}162 163	if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) ||164	    test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {165		/* We're already streaming.  No additional action required */166		snd_ivtv_unlock(itvsc);167		return 0;168	}169 170 171	runtime->hw = snd_ivtv_hw_capture;172	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);173	itvsc->capture_pcm_substream = substream;174	runtime->private_data = itv;175 176	itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data;177 178	/* Not currently streaming, so start it up */179	set_bit(IVTV_F_S_STREAMING, &s->s_flags);180	ret = ivtv_start_v4l2_encode_stream(s);181	snd_ivtv_unlock(itvsc);182 183	return ret;184}185 186static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream)187{188	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);189	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;190	struct ivtv *itv = to_ivtv(v4l2_dev);191	struct ivtv_stream *s;192 193	/* Instruct the ivtv to stop sending packets */194	snd_ivtv_lock(itvsc);195	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];196	ivtv_stop_v4l2_encode_stream(s, 0);197	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);198 199	ivtv_release_stream(s);200 201	itv->pcm_announce_callback = NULL;202	snd_ivtv_unlock(itvsc);203 204	return 0;205}206 207static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream)208{209	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);210 211	itvsc->hwptr_done_capture = 0;212	itvsc->capture_transfer_done = 0;213 214	return 0;215}216 217static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)218{219	return 0;220}221 222static223snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream)224{225	unsigned long flags;226	snd_pcm_uframes_t hwptr_done;227	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);228 229	spin_lock_irqsave(&itvsc->slock, flags);230	hwptr_done = itvsc->hwptr_done_capture;231	spin_unlock_irqrestore(&itvsc->slock, flags);232 233	return hwptr_done;234}235 236static const struct snd_pcm_ops snd_ivtv_pcm_capture_ops = {237	.open		= snd_ivtv_pcm_capture_open,238	.close		= snd_ivtv_pcm_capture_close,239	.prepare	= snd_ivtv_pcm_prepare,240	.trigger	= snd_ivtv_pcm_trigger,241	.pointer	= snd_ivtv_pcm_pointer,242};243 244int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc)245{246	struct snd_pcm *sp;247	struct snd_card *sc = itvsc->sc;248	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;249	struct ivtv *itv = to_ivtv(v4l2_dev);250	int ret;251 252	ret = snd_pcm_new(sc, "CX2341[56] PCM",253			  0, /* PCM device 0, the only one for this card */254			  0, /* 0 playback substreams */255			  1, /* 1 capture substream */256			  &sp);257	if (ret) {258		IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",259			      __func__, ret);260		goto err_exit;261	}262 263	spin_lock_init(&itvsc->slock);264 265	snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,266			&snd_ivtv_pcm_capture_ops);267	snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);268	sp->info_flags = 0;269	sp->private_data = itvsc;270	strscpy(sp->name, itv->card_name, sizeof(sp->name));271 272	return 0;273 274err_exit:275	return ret;276}277