1
2
3
4
5
6
7
8
9
10
11
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/vmalloc.h>
15
16#include <media/v4l2-device.h>
17
18#include <sound/core.h>
19#include <sound/pcm.h>
20
21#include "cx18-driver.h"
22#include "cx18-queue.h"
23#include "cx18-streams.h"
24#include "cx18-fileops.h"
25#include "cx18-alsa.h"
26#include "cx18-alsa-pcm.h"
27
28static unsigned int pcm_debug;
29module_param(pcm_debug, int, 0644);
30MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
31
32#define dprintk(fmt, arg...) do { \
33 if (pcm_debug) \
34 printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \
35 __func__, ##arg); \
36 } while (0)
37
38static const struct snd_pcm_hardware snd_cx18_hw_capture = {
39 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
40 SNDRV_PCM_INFO_MMAP |
41 SNDRV_PCM_INFO_INTERLEAVED |
42 SNDRV_PCM_INFO_MMAP_VALID,
43
44 .formats = SNDRV_PCM_FMTBIT_S16_LE,
45
46 .rates = SNDRV_PCM_RATE_48000,
47
48 .rate_min = 48000,
49 .rate_max = 48000,
50 .channels_min = 2,
51 .channels_max = 2,
52 .buffer_bytes_max = 62720 * 8,
53 .period_bytes_min = 64,
54 .period_bytes_max = 12544,
55 .periods_min = 2,
56 .periods_max = 98,
57};
58
59void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data,
60 size_t num_bytes)
61{
62 struct snd_pcm_substream *substream;
63 struct snd_pcm_runtime *runtime;
64 unsigned int oldptr;
65 unsigned int stride;
66 int period_elapsed = 0;
67 int length;
68
69 dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc,
70 pcm_data, num_bytes);
71
72 substream = cxsc->capture_pcm_substream;
73 if (substream == NULL) {
74 dprintk("substream was NULL\n");
75 return;
76 }
77
78 runtime = substream->runtime;
79 if (runtime == NULL) {
80 dprintk("runtime was NULL\n");
81 return;
82 }
83
84 stride = runtime->frame_bits >> 3;
85 if (stride == 0) {
86 dprintk("stride is zero\n");
87 return;
88 }
89
90 length = num_bytes / stride;
91 if (length == 0) {
92 dprintk("%s: length was zero\n", __func__);
93 return;
94 }
95
96 if (runtime->dma_area == NULL) {
97 dprintk("dma area was NULL - ignoring\n");
98 return;
99 }
100
101 oldptr = cxsc->hwptr_done_capture;
102 if (oldptr + length >= runtime->buffer_size) {
103 unsigned int cnt =
104 runtime->buffer_size - oldptr;
105 memcpy(runtime->dma_area + oldptr * stride, pcm_data,
106 cnt * stride);
107 memcpy(runtime->dma_area, pcm_data + cnt * stride,
108 length * stride - cnt * stride);
109 } else {
110 memcpy(runtime->dma_area + oldptr * stride, pcm_data,
111 length * stride);
112 }
113 snd_pcm_stream_lock(substream);
114
115 cxsc->hwptr_done_capture += length;
116 if (cxsc->hwptr_done_capture >=
117 runtime->buffer_size)
118 cxsc->hwptr_done_capture -=
119 runtime->buffer_size;
120
121 cxsc->capture_transfer_done += length;
122 if (cxsc->capture_transfer_done >=
123 runtime->period_size) {
124 cxsc->capture_transfer_done -=
125 runtime->period_size;
126 period_elapsed = 1;
127 }
128
129 snd_pcm_stream_unlock(substream);
130
131 if (period_elapsed)
132 snd_pcm_period_elapsed(substream);
133}
134
135static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream)
136{
137 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
138 struct snd_pcm_runtime *runtime = substream->runtime;
139 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
140 struct cx18 *cx = to_cx18(v4l2_dev);
141 struct cx18_stream *s;
142 struct cx18_open_id item;
143 int ret;
144
145
146 snd_cx18_lock(cxsc);
147 s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
148
149 item.cx = cx;
150 item.type = s->type;
151 item.open_id = cx->open_id++;
152
153
154 if (cx18_claim_stream(&item, item.type)) {
155
156 snd_cx18_unlock(cxsc);
157 return -EBUSY;
158 }
159
160 if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
161 test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
162
163 snd_cx18_unlock(cxsc);
164 return 0;
165 }
166
167
168 runtime->hw = snd_cx18_hw_capture;
169 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
170 cxsc->capture_pcm_substream = substream;
171 runtime->private_data = cx;
172
173 cx->pcm_announce_callback = cx18_alsa_announce_pcm_data;
174
175
176 set_bit(CX18_F_S_STREAMING, &s->s_flags);
177 ret = cx18_start_v4l2_encode_stream(s);
178 snd_cx18_unlock(cxsc);
179
180 return ret;
181}
182
183static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
184{
185 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
186 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
187 struct cx18 *cx = to_cx18(v4l2_dev);
188 struct cx18_stream *s;
189
190
191 snd_cx18_lock(cxsc);
192 s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
193 cx18_stop_v4l2_encode_stream(s, 0);
194 clear_bit(CX18_F_S_STREAMING, &s->s_flags);
195
196 cx18_release_stream(s);
197
198 cx->pcm_announce_callback = NULL;
199 snd_cx18_unlock(cxsc);
200
201 return 0;
202}
203
204static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream,
205 unsigned int cmd, void *arg)
206{
207 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
208 int ret;
209
210 snd_cx18_lock(cxsc);
211 ret = snd_pcm_lib_ioctl(substream, cmd, arg);
212 snd_cx18_unlock(cxsc);
213 return ret;
214}
215
216
217static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
218 size_t size)
219{
220 struct snd_pcm_runtime *runtime = subs->runtime;
221
222 dprintk("Allocating vbuffer\n");
223 if (runtime->dma_area) {
224 if (runtime->dma_bytes > size)
225 return 0;
226
227 vfree(runtime->dma_area);
228 }
229 runtime->dma_area = vmalloc(size);
230 if (!runtime->dma_area)
231 return -ENOMEM;
232
233 runtime->dma_bytes = size;
234
235 return 0;
236}
237
238static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream,
239 struct snd_pcm_hw_params *params)
240{
241 dprintk("%s called\n", __func__);
242
243 return snd_pcm_alloc_vmalloc_buffer(substream,
244 params_buffer_bytes(params));
245}
246
247static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
248{
249 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
250 unsigned long flags;
251 unsigned char *dma_area = NULL;
252
253 spin_lock_irqsave(&cxsc->slock, flags);
254 if (substream->runtime->dma_area) {
255 dprintk("freeing pcm capture region\n");
256 dma_area = substream->runtime->dma_area;
257 substream->runtime->dma_area = NULL;
258 }
259 spin_unlock_irqrestore(&cxsc->slock, flags);
260 vfree(dma_area);
261
262 return 0;
263}
264
265static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream)
266{
267 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
268
269 cxsc->hwptr_done_capture = 0;
270 cxsc->capture_transfer_done = 0;
271
272 return 0;
273}
274
275static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
276{
277 return 0;
278}
279
280static
281snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream)
282{
283 unsigned long flags;
284 snd_pcm_uframes_t hwptr_done;
285 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
286
287 spin_lock_irqsave(&cxsc->slock, flags);
288 hwptr_done = cxsc->hwptr_done_capture;
289 spin_unlock_irqrestore(&cxsc->slock, flags);
290
291 return hwptr_done;
292}
293
294static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
295 unsigned long offset)
296{
297 void *pageptr = subs->runtime->dma_area + offset;
298
299 return vmalloc_to_page(pageptr);
300}
301
302static const struct snd_pcm_ops snd_cx18_pcm_capture_ops = {
303 .open = snd_cx18_pcm_capture_open,
304 .close = snd_cx18_pcm_capture_close,
305 .ioctl = snd_cx18_pcm_ioctl,
306 .hw_params = snd_cx18_pcm_hw_params,
307 .hw_free = snd_cx18_pcm_hw_free,
308 .prepare = snd_cx18_pcm_prepare,
309 .trigger = snd_cx18_pcm_trigger,
310 .pointer = snd_cx18_pcm_pointer,
311 .page = snd_pcm_get_vmalloc_page,
312};
313
314int snd_cx18_pcm_create(struct snd_cx18_card *cxsc)
315{
316 struct snd_pcm *sp;
317 struct snd_card *sc = cxsc->sc;
318 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
319 struct cx18 *cx = to_cx18(v4l2_dev);
320 int ret;
321
322 ret = snd_pcm_new(sc, "CX23418 PCM",
323 0,
324 0,
325 1,
326 &sp);
327 if (ret) {
328 CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
329 __func__, ret);
330 goto err_exit;
331 }
332
333 spin_lock_init(&cxsc->slock);
334
335 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
336 &snd_cx18_pcm_capture_ops);
337 sp->info_flags = 0;
338 sp->private_data = cxsc;
339 strscpy(sp->name, cx->card_name, sizeof(sp->name));
340
341 return 0;
342
343err_exit:
344 return ret;
345}
346