13f858c41836055399600f36df183eb4204cf017
[sfrench/cifs-2.6.git] / drivers / media / pci / cx18 / cx18-alsa-pcm.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  ALSA PCM device for the
4  *  ALSA interface to cx18 PCM capture streams
5  *
6  *  Copyright (C) 2009  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.
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
28 static unsigned int pcm_debug;
29 module_param(pcm_debug, int, 0644);
30 MODULE_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
38 static 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,  /* just about the value in usbaudio.c */
53         .period_bytes_min = 64,         /* 12544/2, */
54         .period_bytes_max = 12544,
55         .periods_min = 2,
56         .periods_max = 98,              /* 12544, */
57 };
58
59 void 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
135 static 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         /* Instruct the cx18 to start sending packets */
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         /* See if the stream is available */
154         if (cx18_claim_stream(&item, item.type)) {
155                 /* No, it's already in use */
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                 /* We're already streaming.  No additional action required */
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         /* Not currently streaming, so start it up */
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
183 static 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         /* Instruct the cx18 to stop sending packets */
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
204 static 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
217 static 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
238 static 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
247 static 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
265 static 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
275 static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
276 {
277         return 0;
278 }
279
280 static
281 snd_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
294 static 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
302 static 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
314 int 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, /* PCM device 0, the only one for this card */
324                           0, /* 0 playback substreams */
325                           1, /* 1 capture substream */
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
343 err_exit:
344         return ret;
345 }