aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-fileops.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-04-28 19:24:33 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 17:41:41 -0400
commit1c1e45d17b663d4749af456ab7c2fc1f36405ef8 (patch)
tree03704d6fd888c4c617baa81a60df0d80815b2607 /drivers/media/video/cx18/cx18-fileops.c
parentd74bee8b4776b5051c650a90f49a2022d46d8588 (diff)
V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from Conexant for their support. I am in particular thankful to Hauppauge since without their help this driver would not exist. It should also be noted that Steve did the work to get the DVB part up and running. Thank you! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: G. Andrew Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx18/cx18-fileops.c')
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
new file mode 100644
index 000000000000..69303065a294
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -0,0 +1,711 @@
1/*
2 * cx18 file operation functions
3 *
4 * Derived from ivtv-fileops.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-i2c.h"
27#include "cx18-queue.h"
28#include "cx18-vbi.h"
29#include "cx18-audio.h"
30#include "cx18-mailbox.h"
31#include "cx18-scb.h"
32#include "cx18-streams.h"
33#include "cx18-controls.h"
34#include "cx18-ioctl.h"
35#include "cx18-cards.h"
36
37/* This function tries to claim the stream for a specific file descriptor.
38 If no one else is using this stream then the stream is claimed and
39 associated VBI streams are also automatically claimed.
40 Possible error returns: -EBUSY if someone else has claimed
41 the stream or 0 on success. */
42int cx18_claim_stream(struct cx18_open_id *id, int type)
43{
44 struct cx18 *cx = id->cx;
45 struct cx18_stream *s = &cx->streams[type];
46 struct cx18_stream *s_vbi;
47 int vbi_type;
48
49 if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
50 /* someone already claimed this stream */
51 if (s->id == id->open_id) {
52 /* yes, this file descriptor did. So that's OK. */
53 return 0;
54 }
55 if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) {
56 /* VBI is handled already internally, now also assign
57 the file descriptor to this stream for external
58 reading of the stream. */
59 s->id = id->open_id;
60 CX18_DEBUG_INFO("Start Read VBI\n");
61 return 0;
62 }
63 /* someone else is using this stream already */
64 CX18_DEBUG_INFO("Stream %d is busy\n", type);
65 return -EBUSY;
66 }
67 s->id = id->open_id;
68
69 /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
70 CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
71 (provided VBI insertion is on and sliced VBI is selected), for all
72 other streams we're done */
73 if (type == CX18_ENC_STREAM_TYPE_MPG &&
74 cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
75 vbi_type = CX18_ENC_STREAM_TYPE_VBI;
76 } else {
77 return 0;
78 }
79 s_vbi = &cx->streams[vbi_type];
80
81 set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
82
83 /* mark that it is used internally */
84 set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags);
85 return 0;
86}
87
88/* This function releases a previously claimed stream. It will take into
89 account associated VBI streams. */
90void cx18_release_stream(struct cx18_stream *s)
91{
92 struct cx18 *cx = s->cx;
93 struct cx18_stream *s_vbi;
94
95 s->id = -1;
96 if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
97 test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
98 /* this stream is still in use internally */
99 return;
100 }
101 if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
102 CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);
103 return;
104 }
105
106 cx18_flush_queues(s);
107
108 /* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI,
109 for all other streams we're done */
110 if (s->type == CX18_ENC_STREAM_TYPE_MPG)
111 s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
112 else
113 return;
114
115 /* clear internal use flag */
116 if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
117 /* was already cleared */
118 return;
119 }
120 if (s_vbi->id != -1) {
121 /* VBI stream still claimed by a file descriptor */
122 return;
123 }
124 clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
125 cx18_flush_queues(s_vbi);
126}
127
128static void cx18_dualwatch(struct cx18 *cx)
129{
130 struct v4l2_tuner vt;
131 u16 new_bitmap;
132 u16 new_stereo_mode;
133 const u16 stereo_mask = 0x0300;
134 const u16 dual = 0x0200;
135
136 new_stereo_mode = cx->params.audio_properties & stereo_mask;
137 memset(&vt, 0, sizeof(vt));
138 cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
139 if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
140 (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
141 new_stereo_mode = dual;
142
143 if (new_stereo_mode == cx->dualwatch_stereo_mode)
144 return;
145
146 new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask);
147
148 CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
149 cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
150
151 if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
152 cx18_find_handle(cx), new_bitmap) == 0) {
153 cx->dualwatch_stereo_mode = new_stereo_mode;
154 return;
155 }
156 CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
157}
158
159
160static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err)
161{
162 struct cx18 *cx = s->cx;
163 struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
164 struct cx18_buffer *buf;
165 DEFINE_WAIT(wait);
166
167 *err = 0;
168 while (1) {
169 if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
170
171 if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
172 cx->dualwatch_jiffies = jiffies;
173 cx18_dualwatch(cx);
174 }
175 if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
176 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
177 while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
178 /* byteswap and process VBI data */
179/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
180 cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
181 }
182 }
183 buf = &cx->vbi.sliced_mpeg_buf;
184 if (buf->readpos != buf->bytesused)
185 return buf;
186 }
187
188 /* do we have leftover data? */
189 buf = cx18_dequeue(s, &s->q_io);
190 if (buf)
191 return buf;
192
193 /* do we have new data? */
194 buf = cx18_dequeue(s, &s->q_full);
195 if (buf) {
196 if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP,
197 &buf->b_flags))
198 return buf;
199 if (s->type == CX18_ENC_STREAM_TYPE_MPG)
200 /* byteswap MPG data */
201 cx18_buf_swap(buf);
202 else {
203 /* byteswap and process VBI data */
204 cx18_process_vbi_data(cx, buf,
205 s->dma_pts, s->type);
206 }
207 return buf;
208 }
209
210 /* return if end of stream */
211 if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
212 CX18_DEBUG_INFO("EOS %s\n", s->name);
213 return NULL;
214 }
215
216 /* return if file was opened with O_NONBLOCK */
217 if (non_block) {
218 *err = -EAGAIN;
219 return NULL;
220 }
221
222 /* wait for more data to arrive */
223 prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
224 /* New buffers might have become available before we were added
225 to the waitqueue */
226 if (!s->q_full.buffers)
227 schedule();
228 finish_wait(&s->waitq, &wait);
229 if (signal_pending(current)) {
230 /* return if a signal was received */
231 CX18_DEBUG_INFO("User stopped %s\n", s->name);
232 *err = -EINTR;
233 return NULL;
234 }
235 }
236}
237
238static void cx18_setup_sliced_vbi_buf(struct cx18 *cx)
239{
240 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
241
242 cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];
243 cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];
244 cx->vbi.sliced_mpeg_buf.readpos = 0;
245}
246
247static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
248 struct cx18_buffer *buf, char __user *ubuf, size_t ucount)
249{
250 struct cx18 *cx = s->cx;
251 size_t len = buf->bytesused - buf->readpos;
252
253 if (len > ucount)
254 len = ucount;
255 if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
256 cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
257 const char *start = buf->buf + buf->readpos;
258 const char *p = start + 1;
259 const u8 *q;
260 u8 ch = cx->search_pack_header ? 0xba : 0xe0;
261 int stuffing, i;
262
263 while (start + len > p) {
264 q = memchr(p, 0, start + len - p);
265 if (q == NULL)
266 break;
267 p = q + 1;
268 if ((char *)q + 15 >= buf->buf + buf->bytesused ||
269 q[1] != 0 || q[2] != 1 || q[3] != ch)
270 continue;
271 if (!cx->search_pack_header) {
272 if ((q[6] & 0xc0) != 0x80)
273 continue;
274 if (((q[7] & 0xc0) == 0x80 &&
275 (q[9] & 0xf0) == 0x20) ||
276 ((q[7] & 0xc0) == 0xc0 &&
277 (q[9] & 0xf0) == 0x30)) {
278 ch = 0xba;
279 cx->search_pack_header = 1;
280 p = q + 9;
281 }
282 continue;
283 }
284 stuffing = q[13] & 7;
285 /* all stuffing bytes must be 0xff */
286 for (i = 0; i < stuffing; i++)
287 if (q[14 + i] != 0xff)
288 break;
289 if (i == stuffing &&
290 (q[4] & 0xc4) == 0x44 &&
291 (q[12] & 3) == 3 &&
292 q[14 + stuffing] == 0 &&
293 q[15 + stuffing] == 0 &&
294 q[16 + stuffing] == 1) {
295 cx->search_pack_header = 0;
296 len = (char *)q - start;
297 cx18_setup_sliced_vbi_buf(cx);
298 break;
299 }
300 }
301 }
302 if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
303 CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
304 len, s->name);
305 return -EFAULT;
306 }
307 buf->readpos += len;
308 if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
309 buf != &cx->vbi.sliced_mpeg_buf)
310 cx->mpg_data_received += len;
311 return len;
312}
313
314static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
315 size_t tot_count, int non_block)
316{
317 struct cx18 *cx = s->cx;
318 size_t tot_written = 0;
319 int single_frame = 0;
320
321 if (atomic_read(&cx->capturing) == 0 && s->id == -1) {
322 /* shouldn't happen */
323 CX18_DEBUG_WARN("Stream %s not initialized before read\n",
324 s->name);
325 return -EIO;
326 }
327
328 /* Each VBI buffer is one frame, the v4l2 API says that for VBI the
329 frames should arrive one-by-one, so make sure we never output more
330 than one VBI frame at a time */
331 if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
332 cx->vbi.sliced_in->service_set)
333 single_frame = 1;
334
335 for (;;) {
336 struct cx18_buffer *buf;
337 int rc;
338
339 buf = cx18_get_buffer(s, non_block, &rc);
340 /* if there is no data available... */
341 if (buf == NULL) {
342 /* if we got data, then return that regardless */
343 if (tot_written)
344 break;
345 /* EOS condition */
346 if (rc == 0) {
347 clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
348 clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
349 cx18_release_stream(s);
350 }
351 /* set errno */
352 return rc;
353 }
354
355 rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written,
356 tot_count - tot_written);
357
358 if (buf != &cx->vbi.sliced_mpeg_buf) {
359 if (buf->readpos == buf->bytesused) {
360 cx18_buf_sync_for_device(s, buf);
361 cx18_enqueue(s, buf, &s->q_free);
362 cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
363 s->handle,
364 (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
365 1, buf->id, s->buf_size);
366 } else
367 cx18_enqueue(s, buf, &s->q_io);
368 } else if (buf->readpos == buf->bytesused) {
369 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
370
371 cx->vbi.sliced_mpeg_size[idx] = 0;
372 cx->vbi.inserted_frame++;
373 cx->vbi_data_inserted += buf->bytesused;
374 }
375 if (rc < 0)
376 return rc;
377 tot_written += rc;
378
379 if (tot_written == tot_count || single_frame)
380 break;
381 }
382 return tot_written;
383}
384
385static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf,
386 size_t count, loff_t *pos, int non_block)
387{
388 ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0;
389 struct cx18 *cx = s->cx;
390
391 CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
392 if (rc > 0)
393 pos += rc;
394 return rc;
395}
396
397int cx18_start_capture(struct cx18_open_id *id)
398{
399 struct cx18 *cx = id->cx;
400 struct cx18_stream *s = &cx->streams[id->type];
401 struct cx18_stream *s_vbi;
402
403 if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
404 /* you cannot read from these stream types. */
405 return -EPERM;
406 }
407
408 /* Try to claim this stream. */
409 if (cx18_claim_stream(id, s->type))
410 return -EBUSY;
411
412 /* If capture is already in progress, then we also have to
413 do nothing extra. */
414 if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
415 test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
416 set_bit(CX18_F_S_APPL_IO, &s->s_flags);
417 return 0;
418 }
419
420 /* Start VBI capture if required */
421 s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
422 if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
423 test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
424 !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
425 /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed
426 automatically when the MPG stream is claimed.
427 We only need to start the VBI capturing. */
428 if (cx18_start_v4l2_encode_stream(s_vbi)) {
429 CX18_DEBUG_WARN("VBI capture start failed\n");
430
431 /* Failure, clean up and return an error */
432 clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
433 clear_bit(CX18_F_S_STREAMING, &s->s_flags);
434 /* also releases the associated VBI stream */
435 cx18_release_stream(s);
436 return -EIO;
437 }
438 CX18_DEBUG_INFO("VBI insertion started\n");
439 }
440
441 /* Tell the card to start capturing */
442 if (!cx18_start_v4l2_encode_stream(s)) {
443 /* We're done */
444 set_bit(CX18_F_S_APPL_IO, &s->s_flags);
445 /* Resume a possibly paused encoder */
446 if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
447 cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle);
448 return 0;
449 }
450
451 /* failure, clean up */
452 CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
453
454 /* Note: the CX18_ENC_STREAM_TYPE_VBI is released
455 automatically when the MPG stream is released.
456 We only need to stop the VBI capturing. */
457 if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
458 test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
459 cx18_stop_v4l2_encode_stream(s_vbi, 0);
460 clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
461 }
462 clear_bit(CX18_F_S_STREAMING, &s->s_flags);
463 cx18_release_stream(s);
464 return -EIO;
465}
466
467ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
468 loff_t *pos)
469{
470 struct cx18_open_id *id = filp->private_data;
471 struct cx18 *cx = id->cx;
472 struct cx18_stream *s = &cx->streams[id->type];
473 int rc;
474
475 CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
476
477 mutex_lock(&cx->serialize_lock);
478 rc = cx18_start_capture(id);
479 mutex_unlock(&cx->serialize_lock);
480 if (rc)
481 return rc;
482 return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
483}
484
485unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
486{
487 struct cx18_open_id *id = filp->private_data;
488 struct cx18 *cx = id->cx;
489 struct cx18_stream *s = &cx->streams[id->type];
490 int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
491
492 /* Start a capture if there is none */
493 if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
494 int rc;
495
496 mutex_lock(&cx->serialize_lock);
497 rc = cx18_start_capture(id);
498 mutex_unlock(&cx->serialize_lock);
499 if (rc) {
500 CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
501 s->name, rc);
502 return POLLERR;
503 }
504 CX18_DEBUG_FILE("Encoder poll started capture\n");
505 }
506
507 /* add stream's waitq to the poll list */
508 CX18_DEBUG_HI_FILE("Encoder poll\n");
509 poll_wait(filp, &s->waitq, wait);
510
511 if (s->q_full.length || s->q_io.length)
512 return POLLIN | POLLRDNORM;
513 if (eof)
514 return POLLHUP;
515 return 0;
516}
517
518void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
519{
520 struct cx18 *cx = id->cx;
521 struct cx18_stream *s = &cx->streams[id->type];
522
523 CX18_DEBUG_IOCTL("close() of %s\n", s->name);
524
525 /* 'Unclaim' this stream */
526
527 /* Stop capturing */
528 if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
529 struct cx18_stream *s_vbi =
530 &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
531
532 CX18_DEBUG_INFO("close stopping capture\n");
533 /* Special case: a running VBI capture for VBI insertion
534 in the mpeg stream. Need to stop that too. */
535 if (id->type == CX18_ENC_STREAM_TYPE_MPG &&
536 test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
537 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
538 CX18_DEBUG_INFO("close stopping embedded VBI capture\n");
539 cx18_stop_v4l2_encode_stream(s_vbi, 0);
540 }
541 if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
542 test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
543 /* Also used internally, don't stop capturing */
544 s->id = -1;
545 else
546 cx18_stop_v4l2_encode_stream(s, gop_end);
547 }
548 if (!gop_end) {
549 clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
550 clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
551 cx18_release_stream(s);
552 }
553}
554
555int cx18_v4l2_close(struct inode *inode, struct file *filp)
556{
557 struct cx18_open_id *id = filp->private_data;
558 struct cx18 *cx = id->cx;
559 struct cx18_stream *s = &cx->streams[id->type];
560
561 CX18_DEBUG_IOCTL("close() of %s\n", s->name);
562
563 v4l2_prio_close(&cx->prio, &id->prio);
564
565 /* Easy case first: this stream was never claimed by us */
566 if (s->id != id->open_id) {
567 kfree(id);
568 return 0;
569 }
570
571 /* 'Unclaim' this stream */
572
573 /* Stop radio */
574 mutex_lock(&cx->serialize_lock);
575 if (id->type == CX18_ENC_STREAM_TYPE_RAD) {
576 /* Closing radio device, return to TV mode */
577 cx18_mute(cx);
578 /* Mark that the radio is no longer in use */
579 clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
580 /* Switch tuner to TV */
581 cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
582 /* Select correct audio input (i.e. TV tuner or Line in) */
583 cx18_audio_set_io(cx);
584 if (atomic_read(&cx->capturing) > 0) {
585 /* Undo video mute */
586 cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
587 cx->params.video_mute |
588 (cx->params.video_mute_yuv << 8));
589 }
590 /* Done! Unmute and continue. */
591 cx18_unmute(cx);
592 cx18_release_stream(s);
593 } else {
594 cx18_stop_capture(id, 0);
595 }
596 kfree(id);
597 mutex_unlock(&cx->serialize_lock);
598 return 0;
599}
600
601static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
602{
603 struct cx18 *cx = s->cx;
604 struct cx18_open_id *item;
605
606 CX18_DEBUG_FILE("open %s\n", s->name);
607
608 /* Allocate memory */
609 item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL);
610 if (NULL == item) {
611 CX18_DEBUG_WARN("nomem on v4l2 open\n");
612 return -ENOMEM;
613 }
614 item->cx = cx;
615 item->type = s->type;
616 v4l2_prio_open(&cx->prio, &item->prio);
617
618 item->open_id = cx->open_id++;
619 filp->private_data = item;
620
621 if (item->type == CX18_ENC_STREAM_TYPE_RAD) {
622 /* Try to claim this stream */
623 if (cx18_claim_stream(item, item->type)) {
624 /* No, it's already in use */
625 kfree(item);
626 return -EBUSY;
627 }
628
629 if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
630 if (atomic_read(&cx->capturing) > 0) {
631 /* switching to radio while capture is
632 in progress is not polite */
633 cx18_release_stream(s);
634 kfree(item);
635 return -EBUSY;
636 }
637 }
638
639 /* Mark that the radio is being used. */
640 set_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
641 /* We have the radio */
642 cx18_mute(cx);
643 /* Switch tuner to radio */
644 cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
645 /* Select the correct audio input (i.e. radio tuner) */
646 cx18_audio_set_io(cx);
647 /* Done! Unmute and continue. */
648 cx18_unmute(cx);
649 }
650 return 0;
651}
652
653int cx18_v4l2_open(struct inode *inode, struct file *filp)
654{
655 int res, x, y = 0;
656 struct cx18 *cx = NULL;
657 struct cx18_stream *s = NULL;
658 int minor = iminor(inode);
659
660 /* Find which card this open was on */
661 spin_lock(&cx18_cards_lock);
662 for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
663 /* find out which stream this open was on */
664 for (y = 0; y < CX18_MAX_STREAMS; y++) {
665 s = &cx18_cards[x]->streams[y];
666 if (s->v4l2dev && s->v4l2dev->minor == minor) {
667 cx = cx18_cards[x];
668 break;
669 }
670 }
671 }
672 spin_unlock(&cx18_cards_lock);
673
674 if (cx == NULL) {
675 /* Couldn't find a device registered
676 on that minor, shouldn't happen! */
677 printk(KERN_WARNING "No cx18 device found on minor %d\n",
678 minor);
679 return -ENXIO;
680 }
681
682 mutex_lock(&cx->serialize_lock);
683 if (cx18_init_on_first_open(cx)) {
684 CX18_ERR("Failed to initialize on minor %d\n", minor);
685 mutex_unlock(&cx->serialize_lock);
686 return -ENXIO;
687 }
688 res = cx18_serialized_open(s, filp);
689 mutex_unlock(&cx->serialize_lock);
690 return res;
691}
692
693void cx18_mute(struct cx18 *cx)
694{
695 if (atomic_read(&cx->capturing))
696 cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
697 cx18_find_handle(cx), 1);
698 CX18_DEBUG_INFO("Mute\n");
699}
700
701void cx18_unmute(struct cx18 *cx)
702{
703 if (atomic_read(&cx->capturing)) {
704 cx18_msleep_timeout(100, 0);
705 cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
706 cx18_find_handle(cx), 12);
707 cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
708 cx18_find_handle(cx), 0);
709 }
710 CX18_DEBUG_INFO("Unmute\n");
711}