File Index
Files and directories
Sign in
FILE

ivtv-alsa-pcm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/pci/ivtv/ivtv-alsa-pcm.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  ALSA PCM device for the
  4 *  ALSA interface to ivtv PCM capture streams
  5 *
  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 driver
 10 */
 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	item.itv = itv;
152	item.type = s->type;
153
154	/* See if the stream is available */
155	if (ivtv_claim_stream(&item, item.type)) {
156		/* No, it's already in use */
157		snd_ivtv_unlock(itvsc);
158		return -EBUSY;
159	}
160
161	if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) ||
162	    test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
163		/* We're already streaming.  No additional action required */
164		snd_ivtv_unlock(itvsc);
165		return 0;
166	}
167
168
169	runtime->hw = snd_ivtv_hw_capture;
170	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
171	itvsc->capture_pcm_substream = substream;
172	runtime->private_data = itv;
173
174	itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data;
175
176	/* Not currently streaming, so start it up */
177	set_bit(IVTV_F_S_STREAMING, &s->s_flags);
178	ret = ivtv_start_v4l2_encode_stream(s);
179	snd_ivtv_unlock(itvsc);
180
181	return ret;
182}
183
184static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream)
185{
186	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
187	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
188	struct ivtv *itv = to_ivtv(v4l2_dev);
189	struct ivtv_stream *s;
190
191	/* Instruct the ivtv to stop sending packets */
192	snd_ivtv_lock(itvsc);
193	s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
194	ivtv_stop_v4l2_encode_stream(s, 0);
195	clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
196
197	ivtv_release_stream(s);
198
199	itv->pcm_announce_callback = NULL;
200	snd_ivtv_unlock(itvsc);
201
202	return 0;
203}
204
205static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream)
206{
207	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
208
209	itvsc->hwptr_done_capture = 0;
210	itvsc->capture_transfer_done = 0;
211
212	return 0;
213}
214
215static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
216{
217	return 0;
218}
219
220static
221snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream)
222{
223	unsigned long flags;
224	snd_pcm_uframes_t hwptr_done;
225	struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
226
227	spin_lock_irqsave(&itvsc->slock, flags);
228	hwptr_done = itvsc->hwptr_done_capture;
229	spin_unlock_irqrestore(&itvsc->slock, flags);
230
231	return hwptr_done;
232}
233
234static const struct snd_pcm_ops snd_ivtv_pcm_capture_ops = {
235	.open		= snd_ivtv_pcm_capture_open,
236	.close		= snd_ivtv_pcm_capture_close,
237	.prepare	= snd_ivtv_pcm_prepare,
238	.trigger	= snd_ivtv_pcm_trigger,
239	.pointer	= snd_ivtv_pcm_pointer,
240};
241
242int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc)
243{
244	struct snd_pcm *sp;
245	struct snd_card *sc = itvsc->sc;
246	struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
247	struct ivtv *itv = to_ivtv(v4l2_dev);
248	int ret;
249
250	ret = snd_pcm_new(sc, "CX2341[56] PCM",
251			  0, /* PCM device 0, the only one for this card */
252			  0, /* 0 playback substreams */
253			  1, /* 1 capture substream */
254			  &sp);
255	if (ret) {
256		IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
257			      __func__, ret);
258		goto err_exit;
259	}
260
261	spin_lock_init(&itvsc->slock);
262
263	snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
264			&snd_ivtv_pcm_capture_ops);
265	snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
266	sp->info_flags = 0;
267	sp->private_data = itvsc;
268	strscpy(sp->name, itv->card_name, sizeof(sp->name));
269
270	return 0;
271
272err_exit:
273	return ret;
274}
~/ Read-only file preview.