diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-streams.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c new file mode 100644 index 000000000000..afb141b2027a --- /dev/null +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -0,0 +1,566 @@ | |||
1 | /* | ||
2 | * cx18 init/start/stop/exit stream functions | ||
3 | * | ||
4 | * Derived from ivtv-streams.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include "cx18-driver.h" | ||
25 | #include "cx18-fileops.h" | ||
26 | #include "cx18-mailbox.h" | ||
27 | #include "cx18-i2c.h" | ||
28 | #include "cx18-queue.h" | ||
29 | #include "cx18-ioctl.h" | ||
30 | #include "cx18-streams.h" | ||
31 | #include "cx18-cards.h" | ||
32 | #include "cx18-scb.h" | ||
33 | #include "cx18-av-core.h" | ||
34 | #include "cx18-dvb.h" | ||
35 | |||
36 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C | ||
37 | |||
38 | static struct file_operations cx18_v4l2_enc_fops = { | ||
39 | .owner = THIS_MODULE, | ||
40 | .read = cx18_v4l2_read, | ||
41 | .open = cx18_v4l2_open, | ||
42 | .ioctl = cx18_v4l2_ioctl, | ||
43 | .release = cx18_v4l2_close, | ||
44 | .poll = cx18_v4l2_enc_poll, | ||
45 | }; | ||
46 | |||
47 | /* offset from 0 to register ts v4l2 minors on */ | ||
48 | #define CX18_V4L2_ENC_TS_OFFSET 16 | ||
49 | /* offset from 0 to register pcm v4l2 minors on */ | ||
50 | #define CX18_V4L2_ENC_PCM_OFFSET 24 | ||
51 | /* offset from 0 to register yuv v4l2 minors on */ | ||
52 | #define CX18_V4L2_ENC_YUV_OFFSET 32 | ||
53 | |||
54 | static struct { | ||
55 | const char *name; | ||
56 | int vfl_type; | ||
57 | int minor_offset; | ||
58 | int dma; | ||
59 | enum v4l2_buf_type buf_type; | ||
60 | struct file_operations *fops; | ||
61 | } cx18_stream_info[] = { | ||
62 | { /* CX18_ENC_STREAM_TYPE_MPG */ | ||
63 | "encoder MPEG", | ||
64 | VFL_TYPE_GRABBER, 0, | ||
65 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
66 | &cx18_v4l2_enc_fops | ||
67 | }, | ||
68 | { /* CX18_ENC_STREAM_TYPE_TS */ | ||
69 | "TS", | ||
70 | VFL_TYPE_GRABBER, -1, | ||
71 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
72 | &cx18_v4l2_enc_fops | ||
73 | }, | ||
74 | { /* CX18_ENC_STREAM_TYPE_YUV */ | ||
75 | "encoder YUV", | ||
76 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, | ||
77 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
78 | &cx18_v4l2_enc_fops | ||
79 | }, | ||
80 | { /* CX18_ENC_STREAM_TYPE_VBI */ | ||
81 | "encoder VBI", | ||
82 | VFL_TYPE_VBI, 0, | ||
83 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE, | ||
84 | &cx18_v4l2_enc_fops | ||
85 | }, | ||
86 | { /* CX18_ENC_STREAM_TYPE_PCM */ | ||
87 | "encoder PCM audio", | ||
88 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, | ||
89 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE, | ||
90 | &cx18_v4l2_enc_fops | ||
91 | }, | ||
92 | { /* CX18_ENC_STREAM_TYPE_IDX */ | ||
93 | "encoder IDX", | ||
94 | VFL_TYPE_GRABBER, -1, | ||
95 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
96 | &cx18_v4l2_enc_fops | ||
97 | }, | ||
98 | { /* CX18_ENC_STREAM_TYPE_RAD */ | ||
99 | "encoder radio", | ||
100 | VFL_TYPE_RADIO, 0, | ||
101 | PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE, | ||
102 | &cx18_v4l2_enc_fops | ||
103 | }, | ||
104 | }; | ||
105 | |||
106 | static void cx18_stream_init(struct cx18 *cx, int type) | ||
107 | { | ||
108 | struct cx18_stream *s = &cx->streams[type]; | ||
109 | struct video_device *dev = s->v4l2dev; | ||
110 | u32 max_size = cx->options.megabytes[type] * 1024 * 1024; | ||
111 | |||
112 | /* we need to keep v4l2dev, so restore it afterwards */ | ||
113 | memset(s, 0, sizeof(*s)); | ||
114 | s->v4l2dev = dev; | ||
115 | |||
116 | /* initialize cx18_stream fields */ | ||
117 | s->cx = cx; | ||
118 | s->type = type; | ||
119 | s->name = cx18_stream_info[type].name; | ||
120 | s->handle = 0xffffffff; | ||
121 | |||
122 | s->dma = cx18_stream_info[type].dma; | ||
123 | s->buf_size = cx->stream_buf_size[type]; | ||
124 | if (s->buf_size) | ||
125 | s->buffers = max_size / s->buf_size; | ||
126 | if (s->buffers > 63) { | ||
127 | /* Each stream has a maximum of 63 buffers, | ||
128 | ensure we do not exceed that. */ | ||
129 | s->buffers = 63; | ||
130 | s->buf_size = (max_size / s->buffers) & ~0xfff; | ||
131 | } | ||
132 | spin_lock_init(&s->qlock); | ||
133 | init_waitqueue_head(&s->waitq); | ||
134 | s->id = -1; | ||
135 | cx18_queue_init(&s->q_free); | ||
136 | cx18_queue_init(&s->q_full); | ||
137 | cx18_queue_init(&s->q_io); | ||
138 | } | ||
139 | |||
140 | static int cx18_prep_dev(struct cx18 *cx, int type) | ||
141 | { | ||
142 | struct cx18_stream *s = &cx->streams[type]; | ||
143 | u32 cap = cx->v4l2_cap; | ||
144 | int minor_offset = cx18_stream_info[type].minor_offset; | ||
145 | int minor; | ||
146 | |||
147 | /* These four fields are always initialized. If v4l2dev == NULL, then | ||
148 | this stream is not in use. In that case no other fields but these | ||
149 | four can be used. */ | ||
150 | s->v4l2dev = NULL; | ||
151 | s->cx = cx; | ||
152 | s->type = type; | ||
153 | s->name = cx18_stream_info[type].name; | ||
154 | |||
155 | /* Check whether the radio is supported */ | ||
156 | if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO)) | ||
157 | return 0; | ||
158 | |||
159 | /* Check whether VBI is supported */ | ||
160 | if (type == CX18_ENC_STREAM_TYPE_VBI && | ||
161 | !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) | ||
162 | return 0; | ||
163 | |||
164 | /* card number + user defined offset + device offset */ | ||
165 | minor = cx->num + cx18_first_minor + minor_offset; | ||
166 | |||
167 | /* User explicitly selected 0 buffers for these streams, so don't | ||
168 | create them. */ | ||
169 | if (cx18_stream_info[type].dma != PCI_DMA_NONE && | ||
170 | cx->options.megabytes[type] == 0) { | ||
171 | CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | cx18_stream_init(cx, type); | ||
176 | |||
177 | if (minor_offset == -1) | ||
178 | return 0; | ||
179 | |||
180 | /* allocate and initialize the v4l2 video device structure */ | ||
181 | s->v4l2dev = video_device_alloc(); | ||
182 | if (s->v4l2dev == NULL) { | ||
183 | CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", | ||
184 | s->name); | ||
185 | return -ENOMEM; | ||
186 | } | ||
187 | |||
188 | s->v4l2dev->type = | ||
189 | VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | | ||
190 | VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; | ||
191 | snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s", | ||
192 | cx->num, s->name); | ||
193 | |||
194 | s->v4l2dev->minor = minor; | ||
195 | s->v4l2dev->dev = &cx->dev->dev; | ||
196 | s->v4l2dev->fops = cx18_stream_info[type].fops; | ||
197 | s->v4l2dev->release = video_device_release; | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | /* Initialize v4l2 variables and register v4l2 devices */ | ||
203 | int cx18_streams_setup(struct cx18 *cx) | ||
204 | { | ||
205 | int type; | ||
206 | |||
207 | /* Setup V4L2 Devices */ | ||
208 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | ||
209 | /* Prepare device */ | ||
210 | if (cx18_prep_dev(cx, type)) | ||
211 | break; | ||
212 | |||
213 | /* Allocate Stream */ | ||
214 | if (cx18_stream_alloc(&cx->streams[type])) | ||
215 | break; | ||
216 | } | ||
217 | if (type == CX18_MAX_STREAMS) | ||
218 | return 0; | ||
219 | |||
220 | /* One or more streams could not be initialized. Clean 'em all up. */ | ||
221 | cx18_streams_cleanup(cx); | ||
222 | return -ENOMEM; | ||
223 | } | ||
224 | |||
225 | static int cx18_reg_dev(struct cx18 *cx, int type) | ||
226 | { | ||
227 | struct cx18_stream *s = &cx->streams[type]; | ||
228 | int vfl_type = cx18_stream_info[type].vfl_type; | ||
229 | int minor; | ||
230 | |||
231 | /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something? | ||
232 | * We need a VFL_TYPE_TS defined. | ||
233 | */ | ||
234 | if (strcmp("TS", s->name) == 0) { | ||
235 | /* just return if no DVB is supported */ | ||
236 | if ((cx->card->hw_all & CX18_HW_DVB) == 0) | ||
237 | return 0; | ||
238 | if (cx18_dvb_register(s) < 0) { | ||
239 | CX18_ERR("DVB failed to register\n"); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | if (s->v4l2dev == NULL) | ||
245 | return 0; | ||
246 | |||
247 | minor = s->v4l2dev->minor; | ||
248 | |||
249 | /* Register device. First try the desired minor, then any free one. */ | ||
250 | if (video_register_device(s->v4l2dev, vfl_type, minor) && | ||
251 | video_register_device(s->v4l2dev, vfl_type, -1)) { | ||
252 | CX18_ERR("Couldn't register v4l2 device for %s minor %d\n", | ||
253 | s->name, minor); | ||
254 | video_device_release(s->v4l2dev); | ||
255 | s->v4l2dev = NULL; | ||
256 | return -ENOMEM; | ||
257 | } | ||
258 | minor = s->v4l2dev->minor; | ||
259 | |||
260 | switch (vfl_type) { | ||
261 | case VFL_TYPE_GRABBER: | ||
262 | CX18_INFO("Registered device video%d for %s (%d MB)\n", | ||
263 | minor, s->name, cx->options.megabytes[type]); | ||
264 | break; | ||
265 | |||
266 | case VFL_TYPE_RADIO: | ||
267 | CX18_INFO("Registered device radio%d for %s\n", | ||
268 | minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); | ||
269 | break; | ||
270 | |||
271 | case VFL_TYPE_VBI: | ||
272 | if (cx->options.megabytes[type]) | ||
273 | CX18_INFO("Registered device vbi%d for %s (%d MB)\n", | ||
274 | minor - MINOR_VFL_TYPE_VBI_MIN, | ||
275 | s->name, cx->options.megabytes[type]); | ||
276 | else | ||
277 | CX18_INFO("Registered device vbi%d for %s\n", | ||
278 | minor - MINOR_VFL_TYPE_VBI_MIN, s->name); | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | /* Register v4l2 devices */ | ||
286 | int cx18_streams_register(struct cx18 *cx) | ||
287 | { | ||
288 | int type; | ||
289 | int err = 0; | ||
290 | |||
291 | /* Register V4L2 devices */ | ||
292 | for (type = 0; type < CX18_MAX_STREAMS; type++) | ||
293 | err |= cx18_reg_dev(cx, type); | ||
294 | |||
295 | if (err == 0) | ||
296 | return 0; | ||
297 | |||
298 | /* One or more streams could not be initialized. Clean 'em all up. */ | ||
299 | cx18_streams_cleanup(cx); | ||
300 | return -ENOMEM; | ||
301 | } | ||
302 | |||
303 | /* Unregister v4l2 devices */ | ||
304 | void cx18_streams_cleanup(struct cx18 *cx) | ||
305 | { | ||
306 | struct video_device *vdev; | ||
307 | int type; | ||
308 | |||
309 | /* Teardown all streams */ | ||
310 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | ||
311 | if (cx->streams[type].dvb.enabled) | ||
312 | cx18_dvb_unregister(&cx->streams[type]); | ||
313 | |||
314 | vdev = cx->streams[type].v4l2dev; | ||
315 | |||
316 | cx->streams[type].v4l2dev = NULL; | ||
317 | if (vdev == NULL) | ||
318 | continue; | ||
319 | |||
320 | cx18_stream_free(&cx->streams[type]); | ||
321 | |||
322 | /* Unregister device */ | ||
323 | video_unregister_device(vdev); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | static void cx18_vbi_setup(struct cx18_stream *s) | ||
328 | { | ||
329 | struct cx18 *cx = s->cx; | ||
330 | int raw = cx->vbi.sliced_in->service_set == 0; | ||
331 | u32 data[CX2341X_MBOX_MAX_DATA]; | ||
332 | int lines; | ||
333 | |||
334 | if (cx->is_60hz) { | ||
335 | cx->vbi.count = 12; | ||
336 | cx->vbi.start[0] = 10; | ||
337 | cx->vbi.start[1] = 273; | ||
338 | } else { /* PAL/SECAM */ | ||
339 | cx->vbi.count = 18; | ||
340 | cx->vbi.start[0] = 6; | ||
341 | cx->vbi.start[1] = 318; | ||
342 | } | ||
343 | |||
344 | /* setup VBI registers */ | ||
345 | cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); | ||
346 | |||
347 | /* determine number of lines and total number of VBI bytes. | ||
348 | A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 | ||
349 | The '- 1' byte is probably an unused U or V byte. Or something... | ||
350 | A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal | ||
351 | header, 42 data bytes + checksum (to be confirmed) */ | ||
352 | if (raw) { | ||
353 | lines = cx->vbi.count * 2; | ||
354 | } else { | ||
355 | lines = cx->is_60hz ? 24 : 38; | ||
356 | if (cx->is_60hz) | ||
357 | lines += 2; | ||
358 | } | ||
359 | |||
360 | cx->vbi.enc_size = lines * | ||
361 | (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); | ||
362 | |||
363 | data[0] = s->handle; | ||
364 | /* Lines per field */ | ||
365 | data[1] = (lines / 2) | ((lines / 2) << 16); | ||
366 | /* bytes per line */ | ||
367 | data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); | ||
368 | /* Every X number of frames a VBI interrupt arrives | ||
369 | (frames as in 25 or 30 fps) */ | ||
370 | data[3] = 1; | ||
371 | /* Setup VBI for the cx25840 digitizer */ | ||
372 | if (raw) { | ||
373 | data[4] = 0x20602060; | ||
374 | data[5] = 0x30703070; | ||
375 | } else { | ||
376 | data[4] = 0xB0F0B0F0; | ||
377 | data[5] = 0xA0E0A0E0; | ||
378 | } | ||
379 | |||
380 | CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", | ||
381 | data[0], data[1], data[2], data[3], data[4], data[5]); | ||
382 | |||
383 | if (s->type == CX18_ENC_STREAM_TYPE_VBI) | ||
384 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); | ||
385 | } | ||
386 | |||
387 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | ||
388 | { | ||
389 | u32 data[MAX_MB_ARGUMENTS]; | ||
390 | struct cx18 *cx = s->cx; | ||
391 | struct list_head *p; | ||
392 | int ts = 0; | ||
393 | int captype = 0; | ||
394 | |||
395 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) | ||
396 | return -EINVAL; | ||
397 | |||
398 | CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); | ||
399 | |||
400 | switch (s->type) { | ||
401 | case CX18_ENC_STREAM_TYPE_MPG: | ||
402 | captype = CAPTURE_CHANNEL_TYPE_MPEG; | ||
403 | cx->mpg_data_received = cx->vbi_data_inserted = 0; | ||
404 | cx->dualwatch_jiffies = jiffies; | ||
405 | cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300; | ||
406 | cx->search_pack_header = 0; | ||
407 | break; | ||
408 | |||
409 | case CX18_ENC_STREAM_TYPE_TS: | ||
410 | captype = CAPTURE_CHANNEL_TYPE_TS; | ||
411 | ts = 1; | ||
412 | break; | ||
413 | case CX18_ENC_STREAM_TYPE_YUV: | ||
414 | captype = CAPTURE_CHANNEL_TYPE_YUV; | ||
415 | break; | ||
416 | case CX18_ENC_STREAM_TYPE_PCM: | ||
417 | captype = CAPTURE_CHANNEL_TYPE_PCM; | ||
418 | break; | ||
419 | case CX18_ENC_STREAM_TYPE_VBI: | ||
420 | captype = cx->vbi.sliced_in->service_set ? | ||
421 | CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI; | ||
422 | cx->vbi.frame = 0; | ||
423 | cx->vbi.inserted_frame = 0; | ||
424 | memset(cx->vbi.sliced_mpeg_size, | ||
425 | 0, sizeof(cx->vbi.sliced_mpeg_size)); | ||
426 | break; | ||
427 | default: | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | s->buffers_stolen = 0; | ||
431 | |||
432 | /* mute/unmute video */ | ||
433 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, | ||
434 | s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)); | ||
435 | |||
436 | /* Clear Streamoff flags in case left from last capture */ | ||
437 | clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
438 | |||
439 | cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); | ||
440 | s->handle = data[0]; | ||
441 | cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); | ||
442 | |||
443 | if (atomic_read(&cx->capturing) == 0 && !ts) { | ||
444 | /* Stuff from Windows, we don't know what it is */ | ||
445 | cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); | ||
446 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); | ||
447 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); | ||
448 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); | ||
449 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); | ||
450 | |||
451 | cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, | ||
452 | s->handle, cx->digitizer, cx->digitizer); | ||
453 | |||
454 | /* Setup VBI */ | ||
455 | if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) | ||
456 | cx18_vbi_setup(s); | ||
457 | |||
458 | /* assign program index info. | ||
459 | Mask 7: select I/P/B, Num_req: 400 max */ | ||
460 | cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); | ||
461 | |||
462 | /* Setup API for Stream */ | ||
463 | cx2341x_update(cx, cx18_api_func, NULL, &cx->params); | ||
464 | } | ||
465 | |||
466 | if (atomic_read(&cx->capturing) == 0) { | ||
467 | clear_bit(CX18_F_I_EOS, &cx->i_flags); | ||
468 | write_reg(7, CX18_DSP0_INTERRUPT_MASK); | ||
469 | } | ||
470 | |||
471 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, | ||
472 | (void *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, | ||
473 | (void *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); | ||
474 | |||
475 | list_for_each(p, &s->q_free.list) { | ||
476 | struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); | ||
477 | |||
478 | writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); | ||
479 | writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | ||
480 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, | ||
481 | (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1, | ||
482 | buf->id, s->buf_size); | ||
483 | } | ||
484 | /* begin_capture */ | ||
485 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { | ||
486 | CX18_DEBUG_WARN("Error starting capture!\n"); | ||
487 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | ||
488 | return -EINVAL; | ||
489 | } | ||
490 | |||
491 | /* you're live! sit back and await interrupts :) */ | ||
492 | atomic_inc(&cx->capturing); | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | void cx18_stop_all_captures(struct cx18 *cx) | ||
497 | { | ||
498 | int i; | ||
499 | |||
500 | for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { | ||
501 | struct cx18_stream *s = &cx->streams[i]; | ||
502 | |||
503 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) | ||
504 | continue; | ||
505 | if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) | ||
506 | cx18_stop_v4l2_encode_stream(s, 0); | ||
507 | } | ||
508 | } | ||
509 | |||
510 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | ||
511 | { | ||
512 | struct cx18 *cx = s->cx; | ||
513 | unsigned long then; | ||
514 | |||
515 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) | ||
516 | return -EINVAL; | ||
517 | |||
518 | /* This function assumes that you are allowed to stop the capture | ||
519 | and that we are actually capturing */ | ||
520 | |||
521 | CX18_DEBUG_INFO("Stop Capture\n"); | ||
522 | |||
523 | if (atomic_read(&cx->capturing) == 0) | ||
524 | return 0; | ||
525 | |||
526 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | ||
527 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); | ||
528 | else | ||
529 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); | ||
530 | |||
531 | then = jiffies; | ||
532 | |||
533 | if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { | ||
534 | CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); | ||
535 | } | ||
536 | |||
537 | atomic_dec(&cx->capturing); | ||
538 | |||
539 | /* Clear capture and no-read bits */ | ||
540 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); | ||
541 | |||
542 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | ||
543 | s->handle = 0xffffffff; | ||
544 | |||
545 | if (atomic_read(&cx->capturing) > 0) | ||
546 | return 0; | ||
547 | |||
548 | write_reg(5, CX18_DSP0_INTERRUPT_MASK); | ||
549 | wake_up(&s->waitq); | ||
550 | |||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | u32 cx18_find_handle(struct cx18 *cx) | ||
555 | { | ||
556 | int i; | ||
557 | |||
558 | /* find first available handle to be used for global settings */ | ||
559 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | ||
560 | struct cx18_stream *s = &cx->streams[i]; | ||
561 | |||
562 | if (s->v4l2dev && s->handle) | ||
563 | return s->handle; | ||
564 | } | ||
565 | return 0; | ||
566 | } | ||