2009-03-04 01:37:50 +08:00
|
|
|
/*
|
2009-03-04 00:31:36 +08:00
|
|
|
cx231xx-video.c - driver for Conexant Cx23100/101/102
|
|
|
|
USB video capture devices
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
2009-03-03 17:14:34 +08:00
|
|
|
Based on em28xx driver
|
|
|
|
Based on cx23885 driver
|
|
|
|
Based on cx88 driver
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/bitmap.h>
|
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/mutex.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
#include <media/v4l2-common.h>
|
|
|
|
#include <media/v4l2-ioctl.h>
|
2012-09-17 18:31:04 +08:00
|
|
|
#include <media/v4l2-event.h>
|
2009-03-04 01:37:50 +08:00
|
|
|
#include <media/msp3400.h>
|
|
|
|
#include <media/tuner.h>
|
|
|
|
|
|
|
|
#include "dvb_frontend.h"
|
|
|
|
|
|
|
|
#include "cx231xx.h"
|
|
|
|
#include "cx231xx-vbi.h"
|
|
|
|
|
[media] Stop using linux/version.h on most video drivers
All the modified drivers didn't have any version increment since
Jan, 1 2011. Several of them didn't have any version increment
for a long time, even having new features and important bug fixes
happening.
As we're now filling the QUERYCAP version with the current Kernel
Release, we don't need to maintain a per-driver version control
anymore. So, let's just use the default.
In order to preserve the Kernel module version history, a
KERNEL_VERSION() macro were added to all modified drivers, and
the extraver number were incremented.
I opted to preserve the per-driver version control to a few
pwc, pvrusb2, s2255, s5p-fimc and sh_vou.
A few drivers are still using the legacy way to handle ioctl's.
So, we can't do such change on them, otherwise, they'll break.
Those are: uvc, et61x251 and sn9c102.
The rationale is that the per-driver version control seems to be
actively maintained on those.
Yet, I think that the better for them would be to just use the
default version numbering, instead of doing that by themselves.
While here, removed a few uneeded include linux/version.h
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-06-25 01:45:49 +08:00
|
|
|
#define CX231XX_VERSION "0.0.2"
|
2009-03-13 18:41:58 +08:00
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
|
|
|
|
#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
|
|
|
|
|
|
|
|
#define cx231xx_videodbg(fmt, arg...) do {\
|
|
|
|
if (video_debug) \
|
|
|
|
printk(KERN_INFO "%s %s :"fmt, \
|
|
|
|
dev->name, __func__ , ##arg); } while (0)
|
|
|
|
|
|
|
|
static unsigned int isoc_debug;
|
|
|
|
module_param(isoc_debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
|
|
|
|
|
|
|
|
#define cx231xx_isocdbg(fmt, arg...) \
|
|
|
|
do {\
|
|
|
|
if (isoc_debug) { \
|
|
|
|
printk(KERN_INFO "%s %s :"fmt, \
|
|
|
|
dev->name, __func__ , ##arg); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
|
|
MODULE_LICENSE("GPL");
|
[media] Stop using linux/version.h on most video drivers
All the modified drivers didn't have any version increment since
Jan, 1 2011. Several of them didn't have any version increment
for a long time, even having new features and important bug fixes
happening.
As we're now filling the QUERYCAP version with the current Kernel
Release, we don't need to maintain a per-driver version control
anymore. So, let's just use the default.
In order to preserve the Kernel module version history, a
KERNEL_VERSION() macro were added to all modified drivers, and
the extraver number were incremented.
I opted to preserve the per-driver version control to a few
pwc, pvrusb2, s2255, s5p-fimc and sh_vou.
A few drivers are still using the legacy way to handle ioctl's.
So, we can't do such change on them, otherwise, they'll break.
Those are: uvc, et61x251 and sn9c102.
The rationale is that the per-driver version control seems to be
actively maintained on those.
Yet, I think that the better for them would be to just use the
default version numbering, instead of doing that by themselves.
While here, removed a few uneeded include linux/version.h
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-06-25 01:45:49 +08:00
|
|
|
MODULE_VERSION(CX231XX_VERSION);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
|
|
static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
|
|
static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
|
|
static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
module_param_array(card, int, NULL, 0444);
|
2009-03-04 01:37:50 +08:00
|
|
|
module_param_array(video_nr, int, NULL, 0444);
|
|
|
|
module_param_array(vbi_nr, int, NULL, 0444);
|
|
|
|
module_param_array(radio_nr, int, NULL, 0444);
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
MODULE_PARM_DESC(card, "card type");
|
2009-03-04 01:37:50 +08:00
|
|
|
MODULE_PARM_DESC(video_nr, "video device numbers");
|
2009-03-03 17:14:34 +08:00
|
|
|
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
|
2009-03-04 01:37:50 +08:00
|
|
|
MODULE_PARM_DESC(radio_nr, "radio device numbers");
|
|
|
|
|
|
|
|
static unsigned int video_debug;
|
|
|
|
module_param(video_debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
|
|
|
|
|
|
|
|
/* supported video standards */
|
|
|
|
static struct cx231xx_fmt format[] = {
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
.name = "16bpp YUY2, 4:2:2, packed",
|
|
|
|
.fourcc = V4L2_PIX_FMT_YUYV,
|
|
|
|
.depth = 16,
|
|
|
|
.reg = 0,
|
|
|
|
},
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
Video buffer and parser functions
|
|
|
|
------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Announces that a buffer were filled and request the next
|
|
|
|
*/
|
|
|
|
static inline void buffer_filled(struct cx231xx *dev,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_dmaqueue *dma_q,
|
|
|
|
struct cx231xx_buffer *buf)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
/* Advice that buffer was filled */
|
|
|
|
cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
|
|
|
|
buf->vb.state = VIDEOBUF_DONE;
|
|
|
|
buf->vb.field_count++;
|
2012-09-16 02:14:42 +08:00
|
|
|
v4l2_get_timestamp(&buf->vb.ts);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
dev->video_mode.isoc_ctl.buf = NULL;
|
|
|
|
else
|
|
|
|
dev->video_mode.bulk_ctl.buf = NULL;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
list_del(&buf->vb.queue);
|
|
|
|
wake_up(&buf->vb.done);
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
char *errmsg = "Unknown";
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case -ENOENT:
|
|
|
|
errmsg = "unlinked synchronuously";
|
|
|
|
break;
|
|
|
|
case -ECONNRESET:
|
|
|
|
errmsg = "unlinked asynchronuously";
|
|
|
|
break;
|
|
|
|
case -ENOSR:
|
|
|
|
errmsg = "Buffer error (overrun)";
|
|
|
|
break;
|
|
|
|
case -EPIPE:
|
|
|
|
errmsg = "Stalled (device not responding)";
|
|
|
|
break;
|
|
|
|
case -EOVERFLOW:
|
|
|
|
errmsg = "Babble (bad cable?)";
|
|
|
|
break;
|
|
|
|
case -EPROTO:
|
|
|
|
errmsg = "Bit-stuff error (bad cable?)";
|
|
|
|
break;
|
|
|
|
case -EILSEQ:
|
|
|
|
errmsg = "CRC/Timeout (could be anything)";
|
|
|
|
break;
|
|
|
|
case -ETIME:
|
|
|
|
errmsg = "Device does not respond";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (packet < 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
|
2009-03-04 01:37:50 +08:00
|
|
|
} else {
|
|
|
|
cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
packet, status, errmsg);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* video-buf generic routine to get the next available buffer
|
|
|
|
*/
|
|
|
|
static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_buffer **buf)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_video_mode *vmode =
|
|
|
|
container_of(dma_q, struct cx231xx_video_mode, vidq);
|
|
|
|
struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
char *outp;
|
|
|
|
|
|
|
|
if (list_empty(&dma_q->active)) {
|
|
|
|
cx231xx_isocdbg("No active queue to serve\n");
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
dev->video_mode.isoc_ctl.buf = NULL;
|
|
|
|
else
|
|
|
|
dev->video_mode.bulk_ctl.buf = NULL;
|
2009-03-04 01:37:50 +08:00
|
|
|
*buf = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the next buffer */
|
|
|
|
*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
|
|
|
|
|
2011-03-31 09:57:33 +08:00
|
|
|
/* Cleans up buffer - Useful for testing for frame/URB loss */
|
2009-03-04 01:37:50 +08:00
|
|
|
outp = videobuf_to_vmalloc(&(*buf)->vb);
|
|
|
|
memset(outp, 0, (*buf)->vb.size);
|
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
dev->video_mode.isoc_ctl.buf = *buf;
|
|
|
|
else
|
|
|
|
dev->video_mode.bulk_ctl.buf = *buf;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Controls the isoc copy of each urb packet
|
|
|
|
*/
|
|
|
|
static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
|
|
int i, rc = 1;
|
2009-03-04 01:37:50 +08:00
|
|
|
unsigned char *p_buffer;
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 bytes_parsed = 0, buffer_size = 0;
|
|
|
|
u8 sav_eav = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return 0;
|
|
|
|
|
2012-01-10 19:48:50 +08:00
|
|
|
if (dev->state & DEV_DISCONNECTED)
|
2009-03-04 01:37:50 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (urb->status < 0) {
|
|
|
|
print_err_status(dev, -1, urb->status);
|
|
|
|
if (urb->status == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
int status = urb->iso_frame_desc[i].status;
|
|
|
|
|
|
|
|
if (status < 0) {
|
|
|
|
print_err_status(dev, i, status);
|
|
|
|
if (urb->iso_frame_desc[i].status != -EPROTO)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (urb->iso_frame_desc[i].actual_length <= 0) {
|
2009-03-04 01:37:50 +08:00
|
|
|
/* cx231xx_isocdbg("packet %d is empty",i); - spammy */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (urb->iso_frame_desc[i].actual_length >
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.max_pkt_size) {
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_isocdbg("packet bigger than packet size");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* get buffer pointer and length */
|
2009-03-04 01:37:50 +08:00
|
|
|
p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
|
2009-03-03 17:14:34 +08:00
|
|
|
buffer_size = urb->iso_frame_desc[i].actual_length;
|
|
|
|
bytes_parsed = 0;
|
|
|
|
|
|
|
|
if (dma_q->is_partial_line) {
|
2009-03-05 04:49:01 +08:00
|
|
|
/* Handle the case of a partial line */
|
2009-03-03 17:14:34 +08:00
|
|
|
sav_eav = dma_q->last_sav;
|
|
|
|
} else {
|
2009-03-05 04:49:01 +08:00
|
|
|
/* Check for a SAV/EAV overlapping
|
|
|
|
the buffer boundary */
|
2009-03-03 17:14:34 +08:00
|
|
|
sav_eav =
|
|
|
|
cx231xx_find_boundary_SAV_EAV(p_buffer,
|
|
|
|
dma_q->partial_buf,
|
|
|
|
&bytes_parsed);
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
sav_eav &= 0xF0;
|
2009-03-04 00:31:36 +08:00
|
|
|
/* Get the first line if we have some portion of an SAV/EAV from
|
|
|
|
the last buffer or a partial line */
|
2009-03-03 17:14:34 +08:00
|
|
|
if (sav_eav) {
|
2009-03-04 00:31:36 +08:00
|
|
|
bytes_parsed += cx231xx_get_video_line(dev, dma_q,
|
2009-03-05 04:49:01 +08:00
|
|
|
sav_eav, /* SAV/EAV */
|
|
|
|
p_buffer + bytes_parsed, /* p_buffer */
|
|
|
|
buffer_size - bytes_parsed);/* buf size */
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Now parse data that is completely in this buffer */
|
|
|
|
/* dma_q->is_partial_line = 0; */
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
while (bytes_parsed < buffer_size) {
|
|
|
|
u32 bytes_used = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
sav_eav = cx231xx_find_next_SAV_EAV(
|
|
|
|
p_buffer + bytes_parsed, /* p_buffer */
|
|
|
|
buffer_size - bytes_parsed, /* buf size */
|
|
|
|
&bytes_used);/* bytes used to get SAV/EAV */
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
bytes_parsed += bytes_used;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
sav_eav &= 0xF0;
|
|
|
|
if (sav_eav && (bytes_parsed < buffer_size)) {
|
2009-03-04 00:31:36 +08:00
|
|
|
bytes_parsed += cx231xx_get_video_line(dev,
|
2009-03-05 04:49:01 +08:00
|
|
|
dma_q, sav_eav, /* SAV/EAV */
|
|
|
|
p_buffer + bytes_parsed,/* p_buffer */
|
|
|
|
buffer_size - bytes_parsed);/*buf size*/
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
/* Save the last four bytes of the buffer so we can check the
|
|
|
|
buffer boundary condition next time */
|
2009-03-03 17:14:34 +08:00
|
|
|
memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
|
|
|
|
bytes_parsed = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
|
|
|
return rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
|
|
|
|
{
|
|
|
|
struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
|
|
int rc = 1;
|
|
|
|
unsigned char *p_buffer;
|
|
|
|
u32 bytes_parsed = 0, buffer_size = 0;
|
|
|
|
u8 sav_eav = 0;
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return 0;
|
|
|
|
|
2012-01-10 19:48:50 +08:00
|
|
|
if (dev->state & DEV_DISCONNECTED)
|
2010-07-07 05:12:25 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (urb->status < 0) {
|
|
|
|
print_err_status(dev, -1, urb->status);
|
|
|
|
if (urb->status == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (1) {
|
|
|
|
|
|
|
|
/* get buffer pointer and length */
|
|
|
|
p_buffer = urb->transfer_buffer;
|
|
|
|
buffer_size = urb->actual_length;
|
|
|
|
bytes_parsed = 0;
|
|
|
|
|
|
|
|
if (dma_q->is_partial_line) {
|
|
|
|
/* Handle the case of a partial line */
|
|
|
|
sav_eav = dma_q->last_sav;
|
|
|
|
} else {
|
|
|
|
/* Check for a SAV/EAV overlapping
|
|
|
|
the buffer boundary */
|
|
|
|
sav_eav =
|
|
|
|
cx231xx_find_boundary_SAV_EAV(p_buffer,
|
|
|
|
dma_q->partial_buf,
|
|
|
|
&bytes_parsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
sav_eav &= 0xF0;
|
|
|
|
/* Get the first line if we have some portion of an SAV/EAV from
|
|
|
|
the last buffer or a partial line */
|
|
|
|
if (sav_eav) {
|
|
|
|
bytes_parsed += cx231xx_get_video_line(dev, dma_q,
|
|
|
|
sav_eav, /* SAV/EAV */
|
|
|
|
p_buffer + bytes_parsed, /* p_buffer */
|
|
|
|
buffer_size - bytes_parsed);/* buf size */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now parse data that is completely in this buffer */
|
|
|
|
/* dma_q->is_partial_line = 0; */
|
|
|
|
|
|
|
|
while (bytes_parsed < buffer_size) {
|
|
|
|
u32 bytes_used = 0;
|
|
|
|
|
|
|
|
sav_eav = cx231xx_find_next_SAV_EAV(
|
|
|
|
p_buffer + bytes_parsed, /* p_buffer */
|
|
|
|
buffer_size - bytes_parsed, /* buf size */
|
|
|
|
&bytes_used);/* bytes used to get SAV/EAV */
|
|
|
|
|
|
|
|
bytes_parsed += bytes_used;
|
|
|
|
|
|
|
|
sav_eav &= 0xF0;
|
|
|
|
if (sav_eav && (bytes_parsed < buffer_size)) {
|
|
|
|
bytes_parsed += cx231xx_get_video_line(dev,
|
|
|
|
dma_q, sav_eav, /* SAV/EAV */
|
|
|
|
p_buffer + bytes_parsed,/* p_buffer */
|
|
|
|
buffer_size - bytes_parsed);/*buf size*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the last four bytes of the buffer so we can check the
|
|
|
|
buffer boundary condition next time */
|
|
|
|
memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
|
|
|
|
bytes_parsed = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
|
|
|
|
u32 *p_bytes_used)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 bytes_used;
|
|
|
|
u8 boundary_bytes[8];
|
|
|
|
u8 sav_eav = 0;
|
|
|
|
|
|
|
|
*p_bytes_used = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Create an array of the last 4 bytes of the last buffer and the first
|
|
|
|
4 bytes of the current buffer. */
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
memcpy(boundary_bytes, partial_buf, 4);
|
|
|
|
memcpy(boundary_bytes + 4, p_buffer, 4);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Check for the SAV/EAV in the boundary buffer */
|
2009-03-04 00:31:36 +08:00
|
|
|
sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
|
|
|
|
&bytes_used);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (sav_eav) {
|
|
|
|
/* found a boundary SAV/EAV. Updates the bytes used to reflect
|
|
|
|
only those used in the new buffer */
|
|
|
|
*p_bytes_used = bytes_used - 4;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return sav_eav;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
|
2009-03-03 17:14:34 +08:00
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
u8 sav_eav = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
/*
|
|
|
|
* Don't search if the buffer size is less than 4. It causes a page
|
|
|
|
* fault since buffer_size - 4 evaluates to a large number in that
|
|
|
|
* case.
|
|
|
|
*/
|
2009-03-03 17:14:34 +08:00
|
|
|
if (buffer_size < 4) {
|
|
|
|
*p_bytes_used = buffer_size;
|
|
|
|
return 0;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
for (i = 0; i < (buffer_size - 3); i++) {
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if ((p_buffer[i] == 0xFF) &&
|
|
|
|
(p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
*p_bytes_used = i + 4;
|
|
|
|
sav_eav = p_buffer[i + 3];
|
|
|
|
return sav_eav;
|
|
|
|
}
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
*p_bytes_used = buffer_size;
|
|
|
|
return 0;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
u32 cx231xx_get_video_line(struct cx231xx *dev,
|
|
|
|
struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
|
|
|
|
u8 *p_buffer, u32 buffer_size)
|
2009-03-03 17:14:34 +08:00
|
|
|
{
|
|
|
|
u32 bytes_copied = 0;
|
|
|
|
int current_field = -1;
|
|
|
|
|
|
|
|
switch (sav_eav) {
|
|
|
|
case SAV_ACTIVE_VIDEO_FIELD1:
|
2009-03-04 00:31:36 +08:00
|
|
|
/* looking for skipped line which occurred in PAL 720x480 mode.
|
|
|
|
In this case, there will be no active data contained
|
|
|
|
between the SAV and EAV */
|
|
|
|
if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
|
|
|
|
(p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
|
|
|
|
((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
|
|
|
|
(p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
|
|
|
|
(p_buffer[3] == EAV_VBLANK_FIELD1) ||
|
|
|
|
(p_buffer[3] == EAV_VBLANK_FIELD2)))
|
2009-03-03 17:14:34 +08:00
|
|
|
return bytes_copied;
|
|
|
|
current_field = 1;
|
|
|
|
break;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
case SAV_ACTIVE_VIDEO_FIELD2:
|
2009-03-04 00:31:36 +08:00
|
|
|
/* looking for skipped line which occurred in PAL 720x480 mode.
|
|
|
|
In this case, there will be no active data contained between
|
|
|
|
the SAV and EAV */
|
|
|
|
if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
|
|
|
|
(p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
|
|
|
|
((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
|
|
|
|
(p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
|
|
|
|
(p_buffer[3] == EAV_VBLANK_FIELD1) ||
|
|
|
|
(p_buffer[3] == EAV_VBLANK_FIELD2)))
|
2009-03-03 17:14:34 +08:00
|
|
|
return bytes_copied;
|
|
|
|
current_field = 2;
|
|
|
|
break;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->last_sav = sav_eav;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
|
|
|
|
buffer_size, current_field);
|
2009-03-03 17:14:34 +08:00
|
|
|
|
|
|
|
return bytes_copied;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
u32 cx231xx_copy_video_line(struct cx231xx *dev,
|
|
|
|
struct cx231xx_dmaqueue *dma_q, u8 *p_line,
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 length, int field_number)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 bytes_to_copy;
|
|
|
|
struct cx231xx_buffer *buf;
|
|
|
|
u32 _line_size = dev->width * 2;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
if (dma_q->current_field != field_number)
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_reset_video_buffer(dev, dma_q);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* get the buffer pointer */
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
buf = dev->video_mode.isoc_ctl.buf;
|
|
|
|
else
|
|
|
|
buf = dev->video_mode.bulk_ctl.buf;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Remember the field number for next time */
|
|
|
|
dma_q->current_field = field_number;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
bytes_to_copy = dma_q->bytes_left_in_line;
|
|
|
|
if (bytes_to_copy > length)
|
|
|
|
bytes_to_copy = length;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (dma_q->lines_completed >= dma_q->lines_per_field) {
|
|
|
|
dma_q->bytes_left_in_line -= bytes_to_copy;
|
2009-03-04 00:31:36 +08:00
|
|
|
dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
|
|
|
|
0 : 1;
|
2009-03-03 17:14:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->is_partial_line = 1;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* If we don't have a buffer, just return the number of bytes we would
|
|
|
|
have copied if we had a buffer. */
|
|
|
|
if (!buf) {
|
|
|
|
dma_q->bytes_left_in_line -= bytes_to_copy;
|
2009-03-04 00:31:36 +08:00
|
|
|
dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
|
|
|
|
? 0 : 1;
|
2009-03-03 17:14:34 +08:00
|
|
|
return bytes_to_copy;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* copy the data to video buffer */
|
|
|
|
cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->pos += bytes_to_copy;
|
|
|
|
dma_q->bytes_left_in_line -= bytes_to_copy;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (dma_q->bytes_left_in_line == 0) {
|
|
|
|
dma_q->bytes_left_in_line = _line_size;
|
|
|
|
dma_q->lines_completed++;
|
|
|
|
dma_q->is_partial_line = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
|
|
|
|
buffer_filled(dev, dma_q, buf);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->pos = 0;
|
|
|
|
buf = NULL;
|
|
|
|
dma_q->lines_completed = 0;
|
|
|
|
}
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return bytes_to_copy;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
void cx231xx_reset_video_buffer(struct cx231xx *dev,
|
|
|
|
struct cx231xx_dmaqueue *dma_q)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_buffer *buf;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* handle the switch from field 1 to field 2 */
|
|
|
|
if (dma_q->current_field == 1) {
|
2009-03-04 00:31:36 +08:00
|
|
|
if (dma_q->lines_completed >= dma_q->lines_per_field)
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->field1_done = 1;
|
2009-03-04 00:31:36 +08:00
|
|
|
else
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->field1_done = 0;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
buf = dev->video_mode.isoc_ctl.buf;
|
|
|
|
else
|
|
|
|
buf = dev->video_mode.bulk_ctl.buf;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (buf == NULL) {
|
|
|
|
/* first try to get the buffer */
|
|
|
|
get_next_buf(dma_q, &buf);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->pos = 0;
|
|
|
|
dma_q->field1_done = 0;
|
|
|
|
dma_q->current_field = -1;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* reset the counters */
|
|
|
|
dma_q->bytes_left_in_line = dev->width << 1;
|
|
|
|
dma_q->lines_completed = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
2009-03-04 00:31:36 +08:00
|
|
|
u8 *p_buffer, u32 bytes_to_copy)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 *p_out_buffer = NULL;
|
|
|
|
u32 current_line_bytes_copied = 0;
|
|
|
|
struct cx231xx_buffer *buf;
|
|
|
|
u32 _line_size = dev->width << 1;
|
|
|
|
void *startwrite;
|
|
|
|
int offset, lencopy;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
buf = dev->video_mode.isoc_ctl.buf;
|
|
|
|
else
|
|
|
|
buf = dev->video_mode.bulk_ctl.buf;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (buf == NULL)
|
|
|
|
return -1;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
p_out_buffer = videobuf_to_vmalloc(&buf->vb);
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Offset field 2 one line from the top of the buffer */
|
|
|
|
offset = (dma_q->current_field == 1) ? 0 : _line_size;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Offset for field 2 */
|
|
|
|
startwrite = p_out_buffer + offset;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* lines already completed in the current field */
|
|
|
|
startwrite += (dma_q->lines_completed * _line_size * 2);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* bytes already completed in the current line */
|
|
|
|
startwrite += current_line_bytes_copied;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
|
|
|
|
bytes_to_copy : dma_q->bytes_left_in_line;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size))
|
2009-03-03 17:14:34 +08:00
|
|
|
return 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* The below copies the UYVY data straight into video buffer */
|
|
|
|
cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
void cx231xx_swab(u16 *from, u16 *to, u16 len)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u16 i;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (len <= 0)
|
|
|
|
return;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
for (i = 0; i < len / 2; i++)
|
2009-03-03 17:14:34 +08:00
|
|
|
to[i] = (from[i] << 8) | (from[i] >> 8);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 buffer_complete = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Dual field stream */
|
2009-03-04 00:31:36 +08:00
|
|
|
buffer_complete = ((dma_q->current_field == 2) &&
|
|
|
|
(dma_q->lines_completed >= dma_q->lines_per_field) &&
|
|
|
|
dma_q->field1_done);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return buffer_complete;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
Videobuf operations
|
|
|
|
------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
|
|
|
|
{
|
|
|
|
struct cx231xx_fh *fh = vq->priv_data;
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
*size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
|
2009-03-04 01:37:50 +08:00
|
|
|
if (0 == *count)
|
|
|
|
*count = CX231XX_DEF_BUF;
|
|
|
|
|
|
|
|
if (*count < CX231XX_MIN_BUF)
|
|
|
|
*count = CX231XX_MIN_BUF;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is called *without* dev->slock held; please keep it that way */
|
|
|
|
static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = vq->priv_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
unsigned long flags = 0;
|
2009-03-04 00:31:36 +08:00
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
if (in_interrupt())
|
|
|
|
BUG();
|
|
|
|
|
|
|
|
/* We used to wait for the buffer to finish here, but this didn't work
|
|
|
|
because, as we were keeping the state as VIDEOBUF_QUEUED,
|
|
|
|
videobuf_queue_cancel marked it as finished for us.
|
|
|
|
(Also, it could wedge forever if the hardware was misconfigured.)
|
|
|
|
|
|
|
|
This should be safe; by the time we get here, the buffer isn't
|
|
|
|
queued anymore. If we ever start marking the buffers as
|
|
|
|
VIDEOBUF_ACTIVE, it won't be, though.
|
2009-03-03 17:14:34 +08:00
|
|
|
*/
|
2009-03-04 01:37:50 +08:00
|
|
|
spin_lock_irqsave(&dev->video_mode.slock, flags);
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO) {
|
|
|
|
if (dev->video_mode.isoc_ctl.buf == buf)
|
|
|
|
dev->video_mode.isoc_ctl.buf = NULL;
|
|
|
|
} else {
|
|
|
|
if (dev->video_mode.bulk_ctl.buf == buf)
|
|
|
|
dev->video_mode.bulk_ctl.buf = NULL;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
spin_unlock_irqrestore(&dev->video_mode.slock, flags);
|
|
|
|
|
|
|
|
videobuf_vmalloc_free(&buf->vb);
|
|
|
|
buf->vb.state = VIDEOBUF_NEEDS_INIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
|
2009-03-03 17:14:34 +08:00
|
|
|
enum v4l2_field field)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = vq->priv_data;
|
|
|
|
struct cx231xx_buffer *buf =
|
|
|
|
container_of(vb, struct cx231xx_buffer, vb);
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc = 0, urb_init = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* The only currently supported format is 16 bits/pixel */
|
2009-03-04 00:31:36 +08:00
|
|
|
buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
|
|
|
|
+ 7) >> 3;
|
2009-03-03 17:14:34 +08:00
|
|
|
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
|
2009-03-04 01:37:50 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
buf->vb.width = dev->width;
|
2009-03-04 01:37:50 +08:00
|
|
|
buf->vb.height = dev->height;
|
2009-03-03 17:14:34 +08:00
|
|
|
buf->vb.field = field;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
|
|
|
|
rc = videobuf_iolock(vq, &buf->vb, NULL);
|
|
|
|
if (rc < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO) {
|
|
|
|
if (!dev->video_mode.isoc_ctl.num_bufs)
|
|
|
|
urb_init = 1;
|
|
|
|
} else {
|
|
|
|
if (!dev->video_mode.bulk_ctl.num_bufs)
|
|
|
|
urb_init = 1;
|
|
|
|
}
|
|
|
|
/*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
|
|
|
|
urb_init, dev->video_mode.max_pkt_size);*/
|
2009-03-04 01:37:50 +08:00
|
|
|
if (urb_init) {
|
2010-07-07 05:12:25 +08:00
|
|
|
dev->mode_tv = 0;
|
|
|
|
if (dev->USE_ISO)
|
|
|
|
rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
|
2009-03-03 17:14:34 +08:00
|
|
|
CX231XX_NUM_BUFS,
|
|
|
|
dev->video_mode.max_pkt_size,
|
|
|
|
cx231xx_isoc_copy);
|
2010-07-07 05:12:25 +08:00
|
|
|
else
|
|
|
|
rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
|
|
|
|
CX231XX_NUM_BUFS,
|
|
|
|
dev->video_mode.max_pkt_size,
|
|
|
|
cx231xx_bulk_copy);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (rc < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->vb.state = VIDEOBUF_PREPARED;
|
|
|
|
return 0;
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
fail:
|
2009-03-04 01:37:50 +08:00
|
|
|
free_buffer(vq, buf);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_buffer *buf =
|
|
|
|
container_of(vb, struct cx231xx_buffer, vb);
|
|
|
|
struct cx231xx_fh *fh = vq->priv_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
buf->vb.state = VIDEOBUF_QUEUED;
|
|
|
|
list_add_tail(&buf->vb.queue, &vidq->active);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void buffer_release(struct videobuf_queue *vq,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct videobuf_buffer *vb)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_buffer *buf =
|
|
|
|
container_of(vb, struct cx231xx_buffer, vb);
|
|
|
|
struct cx231xx_fh *fh = vq->priv_data;
|
|
|
|
struct cx231xx *dev = (struct cx231xx *)fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
cx231xx_isocdbg("cx231xx: called buffer_release\n");
|
|
|
|
|
|
|
|
free_buffer(vq, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct videobuf_queue_ops cx231xx_video_qops = {
|
2009-03-03 17:14:34 +08:00
|
|
|
.buf_setup = buffer_setup,
|
|
|
|
.buf_prepare = buffer_prepare,
|
|
|
|
.buf_queue = buffer_queue,
|
|
|
|
.buf_release = buffer_release,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/********************* v4l2 interface **************************************/
|
|
|
|
|
|
|
|
void video_mux(struct cx231xx *dev, int index)
|
|
|
|
{
|
|
|
|
dev->video_input = index;
|
|
|
|
dev->ctl_ainput = INPUT(index)->amux;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_set_video_input_mux(dev, index);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-04-02 22:26:22 +08:00
|
|
|
cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_set_audio_input(dev, dev->ctl_ainput);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_info("video_mux : %d\n", index);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* do mode control overrides if required */
|
|
|
|
cx231xx_do_mode_ctrl_overrides(dev);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Usage lock check functions */
|
|
|
|
static int res_get(struct cx231xx_fh *fh)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* This instance already has stream_on */
|
|
|
|
if (fh->stream_on)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
|
|
if (dev->stream_on)
|
|
|
|
return -EBUSY;
|
|
|
|
dev->stream_on = 1;
|
|
|
|
} else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
|
|
|
if (dev->vbi_stream_on)
|
|
|
|
return -EBUSY;
|
|
|
|
dev->vbi_stream_on = 1;
|
|
|
|
} else
|
|
|
|
return -EINVAL;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
fh->stream_on = 1;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int res_check(struct cx231xx_fh *fh)
|
|
|
|
{
|
2009-03-04 00:31:36 +08:00
|
|
|
return fh->stream_on;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void res_free(struct cx231xx_fh *fh)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
fh->stream_on = 0;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
dev->stream_on = 0;
|
|
|
|
if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
|
|
|
|
dev->vbi_stream_on = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int check_dev(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
if (dev->state & DEV_DISCONNECTED) {
|
|
|
|
cx231xx_errdev("v4l2 ioctl: device not present\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
|
|
IOCTL vidioc handling
|
|
|
|
------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_format *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
f->fmt.pix.width = dev->width;
|
|
|
|
f->fmt.pix.height = dev->height;
|
2009-07-03 02:57:09 +08:00
|
|
|
f->fmt.pix.pixelformat = dev->format->fourcc;
|
|
|
|
f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
|
2009-03-03 17:14:34 +08:00
|
|
|
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
|
|
|
|
|
|
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
2013-02-09 17:35:02 +08:00
|
|
|
f->fmt.pix.priv = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(format); i++)
|
|
|
|
if (format[i].fourcc == fourcc)
|
|
|
|
return &format[i];
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_format *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-05-31 08:45:46 +08:00
|
|
|
unsigned int width = f->fmt.pix.width;
|
|
|
|
unsigned int height = f->fmt.pix.height;
|
2009-03-03 17:14:34 +08:00
|
|
|
unsigned int maxw = norm_maxw(dev);
|
|
|
|
unsigned int maxh = norm_maxh(dev);
|
|
|
|
struct cx231xx_fmt *fmt;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
|
|
|
|
if (!fmt) {
|
|
|
|
cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
f->fmt.pix.pixelformat);
|
2009-03-04 01:37:50 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* width must even because of the YUYV format
|
|
|
|
height must be even because of interlacing */
|
2009-05-31 08:45:46 +08:00
|
|
|
v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
f->fmt.pix.width = width;
|
|
|
|
f->fmt.pix.height = height;
|
|
|
|
f->fmt.pix.pixelformat = fmt->fourcc;
|
2013-02-09 17:35:02 +08:00
|
|
|
f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
|
|
|
|
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
|
|
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
2013-02-09 17:35:02 +08:00
|
|
|
f->fmt.pix.priv = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_format *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
|
|
|
struct cx231xx_fmt *fmt;
|
2010-05-09 21:11:01 +08:00
|
|
|
struct v4l2_mbus_framefmt mbus_fmt;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
vidioc_try_fmt_vid_cap(file, priv, f);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
|
2010-10-10 00:13:35 +08:00
|
|
|
if (!fmt)
|
|
|
|
return -EINVAL;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
|
|
|
|
cx231xx_errdev("%s queue busy\n", __func__);
|
2010-10-10 00:13:35 +08:00
|
|
|
return -EBUSY;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->stream_on && !fh->stream_on) {
|
|
|
|
cx231xx_errdev("%s device in use by another fh\n", __func__);
|
2010-10-10 00:13:35 +08:00
|
|
|
return -EBUSY;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set new image size */
|
|
|
|
dev->width = f->fmt.pix.width;
|
|
|
|
dev->height = f->fmt.pix.height;
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->format = fmt;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-05-09 21:11:01 +08:00
|
|
|
v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
|
|
|
|
call_all(dev, video, s_mbus_fmt, &mbus_fmt);
|
|
|
|
v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
*id = dev->norm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-15 17:10:40 +08:00
|
|
|
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2010-07-10 00:29:31 +08:00
|
|
|
struct v4l2_mbus_framefmt mbus_fmt;
|
2009-03-03 17:14:34 +08:00
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2013-03-15 17:10:40 +08:00
|
|
|
if (dev->norm == norm)
|
2013-01-29 21:50:54 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (videobuf_queue_is_busy(&fh->vb_vidq))
|
|
|
|
return -EBUSY;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2013-03-15 17:10:40 +08:00
|
|
|
dev->norm = norm;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* Adjusts width/height, if needed */
|
2013-01-29 21:50:54 +08:00
|
|
|
dev->width = 720;
|
|
|
|
dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-10 00:29:31 +08:00
|
|
|
call_all(dev, core, s_std, dev->norm);
|
|
|
|
|
|
|
|
/* We need to reset basic properties in the decoder related to
|
|
|
|
resolution (since a standard change effects things like the number
|
|
|
|
of lines in VACT, etc) */
|
2013-01-29 21:50:54 +08:00
|
|
|
memset(&mbus_fmt, 0, sizeof(mbus_fmt));
|
|
|
|
mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
|
|
|
|
mbus_fmt.width = dev->width;
|
|
|
|
mbus_fmt.height = dev->height;
|
2010-07-10 00:29:31 +08:00
|
|
|
call_all(dev, video, s_mbus_fmt, &mbus_fmt);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* do mode control overrides */
|
|
|
|
cx231xx_do_mode_ctrl_overrides(dev);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *iname[] = {
|
|
|
|
[CX231XX_VMUX_COMPOSITE1] = "Composite1",
|
2009-03-04 00:31:36 +08:00
|
|
|
[CX231XX_VMUX_SVIDEO] = "S-Video",
|
2009-03-04 01:37:50 +08:00
|
|
|
[CX231XX_VMUX_TELEVISION] = "Television",
|
2009-03-04 00:31:36 +08:00
|
|
|
[CX231XX_VMUX_CABLE] = "Cable TV",
|
|
|
|
[CX231XX_VMUX_DVB] = "DVB",
|
|
|
|
[CX231XX_VMUX_DEBUG] = "for debug only",
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_enum_input(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_input *i)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2011-07-25 04:07:07 +08:00
|
|
|
u32 gen_stat;
|
|
|
|
unsigned int ret, n;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
n = i->index;
|
|
|
|
if (n >= MAX_CX231XX_INPUT)
|
|
|
|
return -EINVAL;
|
|
|
|
if (0 == INPUT(n)->type)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
i->index = n;
|
|
|
|
i->type = V4L2_INPUT_TYPE_CAMERA;
|
|
|
|
|
|
|
|
strcpy(i->name, iname[INPUT(n)->type]);
|
|
|
|
|
|
|
|
if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
|
2009-03-03 17:14:34 +08:00
|
|
|
(CX231XX_VMUX_CABLE == INPUT(n)->type))
|
2009-03-04 01:37:50 +08:00
|
|
|
i->type = V4L2_INPUT_TYPE_TUNER;
|
|
|
|
|
|
|
|
i->std = dev->vdev->tvnorms;
|
|
|
|
|
2011-07-25 04:07:07 +08:00
|
|
|
/* If they are asking about the active input, read signal status */
|
|
|
|
if (n == dev->video_input) {
|
|
|
|
ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
|
|
GEN_STAT, 2, &gen_stat, 4);
|
|
|
|
if (ret > 0) {
|
|
|
|
if ((gen_stat & FLD_VPRES) == 0x00)
|
|
|
|
i->status |= V4L2_IN_ST_NO_SIGNAL;
|
|
|
|
if ((gen_stat & FLD_HLOCK) == 0x00)
|
|
|
|
i->status |= V4L2_IN_ST_NO_H_LOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_g_input(struct file *file, void *priv, unsigned int *i)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
*i = dev->video_input;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_s_input(struct file *file, void *priv, unsigned int i)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
dev->mode_tv = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (i >= MAX_CX231XX_INPUT)
|
|
|
|
return -EINVAL;
|
|
|
|
if (0 == INPUT(i)->type)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
video_mux(dev, i);
|
|
|
|
|
2010-07-13 03:50:30 +08:00
|
|
|
if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
|
|
|
|
INPUT(i)->type == CX231XX_VMUX_CABLE) {
|
|
|
|
/* There's a tuner, so reset the standard and put it on the
|
|
|
|
last known frequency (since it was probably powered down
|
|
|
|
until now */
|
|
|
|
call_all(dev, core, s_std, dev->norm);
|
|
|
|
}
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (0 != t->index)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
strcpy(t->name, "Tuner");
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
t->type = V4L2_TUNER_ANALOG_TV;
|
2009-03-04 01:37:50 +08:00
|
|
|
t->capability = V4L2_TUNER_CAP_NORM;
|
2009-03-03 17:14:34 +08:00
|
|
|
t->rangehigh = 0xffffffffUL;
|
|
|
|
t->signal = 0xffff; /* LOCKED */
|
2012-09-13 23:54:36 +08:00
|
|
|
call_all(dev, tuner, g_tuner, t);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-15 17:10:06 +08:00
|
|
|
int cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (0 != t->index)
|
|
|
|
return -EINVAL;
|
|
|
|
#if 0
|
2009-03-21 10:33:48 +08:00
|
|
|
call_all(dev, tuner, s_tuner, t);
|
2009-03-04 01:37:50 +08:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_g_frequency(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_frequency *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2012-09-13 23:54:36 +08:00
|
|
|
if (f->tuner)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
f->frequency = dev->ctl_freq;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_s_frequency(struct file *file, void *priv,
|
2013-03-19 15:09:26 +08:00
|
|
|
const struct v4l2_frequency *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2013-03-19 15:09:26 +08:00
|
|
|
struct v4l2_frequency new_freq = *f;
|
2009-03-03 17:14:34 +08:00
|
|
|
int rc;
|
2010-07-07 05:12:25 +08:00
|
|
|
u32 if_frequency = 5400000;
|
|
|
|
|
|
|
|
cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
|
|
|
|
f->frequency, f->type);
|
|
|
|
/*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (0 != f->tuner)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set pre channel change settings in DIF first */
|
|
|
|
rc = cx231xx_tuner_pre_channel_change(dev);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
call_all(dev, tuner, s_frequency, f);
|
2013-03-19 15:09:26 +08:00
|
|
|
call_all(dev, tuner, g_frequency, &new_freq);
|
|
|
|
dev->ctl_freq = new_freq.frequency;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set post channel change settings in DIF first */
|
|
|
|
rc = cx231xx_tuner_post_channel_change(dev);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->tuner_type == TUNER_NXP_TDA18271) {
|
|
|
|
if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
|
|
|
|
if_frequency = 5400000; /*5.4MHz */
|
|
|
|
else if (dev->norm & V4L2_STD_B)
|
|
|
|
if_frequency = 6000000; /*6.0MHz */
|
|
|
|
else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
|
|
|
|
if_frequency = 6900000; /*6.9MHz */
|
|
|
|
else if (dev->norm & V4L2_STD_GH)
|
|
|
|
if_frequency = 7100000; /*7.1MHz */
|
|
|
|
else if (dev->norm & V4L2_STD_PAL_I)
|
|
|
|
if_frequency = 7250000; /*7.25MHz */
|
|
|
|
else if (dev->norm & V4L2_STD_SECAM_L)
|
|
|
|
if_frequency = 6900000; /*6.9MHz */
|
|
|
|
else if (dev->norm & V4L2_STD_SECAM_LC)
|
|
|
|
if_frequency = 1250000; /*1.25MHz */
|
|
|
|
|
|
|
|
cx231xx_info("if_frequency is set to %d\n", if_frequency);
|
|
|
|
cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
|
|
|
|
|
|
|
|
update_HH_register_after_set_DIF(dev);
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-05-29 17:59:43 +08:00
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
|
|
|
|
|
|
int cx231xx_g_chip_info(struct file *file, void *fh,
|
|
|
|
struct v4l2_dbg_chip_info *chip)
|
2012-09-13 16:37:11 +08:00
|
|
|
{
|
2013-05-29 17:59:43 +08:00
|
|
|
switch (chip->match.addr) {
|
|
|
|
case 0: /* Cx231xx - internal registers */
|
|
|
|
return 0;
|
|
|
|
case 1: /* AFE - read byte */
|
|
|
|
strlcpy(chip->name, "AFE (byte)", sizeof(chip->name));
|
|
|
|
return 0;
|
|
|
|
case 2: /* Video Block - read byte */
|
|
|
|
strlcpy(chip->name, "Video (byte)", sizeof(chip->name));
|
|
|
|
return 0;
|
|
|
|
case 3: /* I2S block - read byte */
|
|
|
|
strlcpy(chip->name, "I2S (byte)", sizeof(chip->name));
|
|
|
|
return 0;
|
|
|
|
case 4: /* AFE - read dword */
|
|
|
|
strlcpy(chip->name, "AFE (dword)", sizeof(chip->name));
|
|
|
|
return 0;
|
|
|
|
case 5: /* Video Block - read dword */
|
|
|
|
strlcpy(chip->name, "Video (dword)", sizeof(chip->name));
|
|
|
|
return 0;
|
|
|
|
case 6: /* I2S Block - read dword */
|
|
|
|
strlcpy(chip->name, "I2S (dword)", sizeof(chip->name));
|
2012-09-13 16:37:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_g_register(struct file *file, void *priv,
|
2009-03-04 01:37:50 +08:00
|
|
|
struct v4l2_dbg_register *reg)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2013-05-29 17:59:43 +08:00
|
|
|
int ret;
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 value[4] = { 0, 0, 0, 0 };
|
|
|
|
u32 data = 0;
|
|
|
|
|
2013-05-29 17:59:43 +08:00
|
|
|
switch (reg->match.addr) {
|
|
|
|
case 0: /* Cx231xx - internal registers */
|
|
|
|
ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
|
|
|
|
(u16)reg->reg, value, 4);
|
|
|
|
reg->val = value[0] | value[1] << 8 |
|
|
|
|
value[2] << 16 | value[3] << 24;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 4;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
|
|
|
case 1: /* AFE - read byte */
|
|
|
|
ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, &data, 1);
|
|
|
|
reg->val = data;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 1;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
|
|
|
case 2: /* Video Block - read byte */
|
|
|
|
ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, &data, 1);
|
|
|
|
reg->val = data;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 1;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
|
|
|
case 3: /* I2S block - read byte */
|
|
|
|
ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 1, &data, 1);
|
|
|
|
reg->val = data;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 1;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
|
|
|
case 4: /* AFE - read dword */
|
|
|
|
ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, &data, 4);
|
|
|
|
reg->val = data;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 4;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
|
|
|
case 5: /* Video Block - read dword */
|
|
|
|
ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, &data, 4);
|
|
|
|
reg->val = data;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 4;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
|
|
|
case 6: /* I2S Block - read dword */
|
|
|
|
ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 1, &data, 4);
|
|
|
|
reg->val = data;
|
2013-05-29 18:00:04 +08:00
|
|
|
reg->size = 4;
|
2013-05-29 17:59:43 +08:00
|
|
|
break;
|
2009-03-03 17:14:34 +08:00
|
|
|
default:
|
2013-05-29 17:59:43 +08:00
|
|
|
return -EINVAL;
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
2013-05-29 17:59:43 +08:00
|
|
|
return ret < 0 ? ret : 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
int cx231xx_s_register(struct file *file, void *priv,
|
2013-03-24 19:28:46 +08:00
|
|
|
const struct v4l2_dbg_register *reg)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2013-05-29 17:59:43 +08:00
|
|
|
int ret;
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 data[4] = { 0, 0, 0, 0 };
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2013-05-29 17:59:43 +08:00
|
|
|
switch (reg->match.addr) {
|
|
|
|
case 0: /* cx231xx internal registers */
|
|
|
|
data[0] = (u8) reg->val;
|
|
|
|
data[1] = (u8) (reg->val >> 8);
|
|
|
|
data[2] = (u8) (reg->val >> 16);
|
|
|
|
data[3] = (u8) (reg->val >> 24);
|
|
|
|
ret = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
|
|
(u16)reg->reg, data, 4);
|
|
|
|
break;
|
|
|
|
case 1: /* AFE - write byte */
|
|
|
|
ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, reg->val, 1);
|
|
|
|
break;
|
|
|
|
case 2: /* Video Block - write byte */
|
|
|
|
ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, reg->val, 1);
|
|
|
|
break;
|
|
|
|
case 3: /* I2S block - write byte */
|
|
|
|
ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 1, reg->val, 1);
|
|
|
|
break;
|
|
|
|
case 4: /* AFE - write dword */
|
|
|
|
ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, reg->val, 4);
|
|
|
|
break;
|
|
|
|
case 5: /* Video Block - write dword */
|
|
|
|
ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
|
|
(u16)reg->reg, 2, reg->val, 4);
|
2009-03-03 17:14:34 +08:00
|
|
|
break;
|
2013-05-29 17:59:43 +08:00
|
|
|
case 6: /* I2S block - write dword */
|
|
|
|
ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
|
|
(u16)reg->reg, 1, reg->val, 4);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
2013-05-29 17:59:43 +08:00
|
|
|
return ret < 0 ? ret : 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int vidioc_cropcap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_cropcap *cc)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cc->bounds.left = 0;
|
|
|
|
cc->bounds.top = 0;
|
|
|
|
cc->bounds.width = dev->width;
|
|
|
|
cc->bounds.height = dev->height;
|
|
|
|
cc->defrect = cc->bounds;
|
|
|
|
cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
|
|
|
|
cc->pixelaspect.denominator = 59;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_streamon(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
enum v4l2_buf_type type)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
rc = res_get(fh);
|
|
|
|
|
|
|
|
if (likely(rc >= 0))
|
|
|
|
rc = videobuf_streamon(&fh->vb_vidq);
|
|
|
|
|
2009-03-21 10:33:48 +08:00
|
|
|
call_all(dev, video, s_stream, 1);
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_streamoff(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
enum v4l2_buf_type type)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (type != fh->type)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-21 10:33:48 +08:00
|
|
|
cx25840_call(dev, video, s_stream, 0);
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
videobuf_streamoff(&fh->vb_vidq);
|
|
|
|
res_free(fh);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-29 23:52:33 +08:00
|
|
|
int cx231xx_querycap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_capability *cap)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2012-09-13 16:29:12 +08:00
|
|
|
struct video_device *vdev = video_devdata(file);
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
|
|
|
|
strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
|
2009-03-22 19:53:36 +08:00
|
|
|
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2013-01-29 23:32:20 +08:00
|
|
|
if (vdev->vfl_type == VFL_TYPE_RADIO)
|
|
|
|
cap->device_caps = V4L2_CAP_RADIO;
|
|
|
|
else {
|
2012-09-13 17:17:47 +08:00
|
|
|
cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
|
2013-01-29 23:32:20 +08:00
|
|
|
if (vdev->vfl_type == VFL_TYPE_VBI)
|
|
|
|
cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
|
|
|
|
else
|
|
|
|
cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
if (dev->tuner_type != TUNER_ABSENT)
|
2012-09-13 16:29:12 +08:00
|
|
|
cap->device_caps |= V4L2_CAP_TUNER;
|
2012-09-13 17:17:47 +08:00
|
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_READWRITE |
|
2012-09-13 16:29:12 +08:00
|
|
|
V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE |
|
2013-01-29 23:32:20 +08:00
|
|
|
V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
|
|
|
|
if (dev->radio_dev)
|
|
|
|
cap->capabilities |= V4L2_CAP_RADIO;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
|
|
struct v4l2_fmtdesc *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
if (unlikely(f->index >= ARRAY_SIZE(format)))
|
2009-03-04 01:37:50 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
strlcpy(f->description, format[f->index].name, sizeof(f->description));
|
|
|
|
f->pixelformat = format[f->index].fourcc;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RAW VBI ioctls */
|
|
|
|
|
|
|
|
static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_format *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2013-02-09 17:41:11 +08:00
|
|
|
|
2010-07-09 00:12:47 +08:00
|
|
|
f->fmt.vbi.sampling_rate = 6750000 * 4;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
|
|
|
|
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
|
2010-07-09 00:12:47 +08:00
|
|
|
f->fmt.vbi.offset = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
|
2009-03-03 17:14:34 +08:00
|
|
|
PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
|
2009-03-03 17:14:34 +08:00
|
|
|
PAL_VBI_LINES : NTSC_VBI_LINES;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
|
2009-03-03 17:14:34 +08:00
|
|
|
PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
|
2013-02-09 17:41:11 +08:00
|
|
|
memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct v4l2_format *f)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2010-07-09 00:12:47 +08:00
|
|
|
f->fmt.vbi.sampling_rate = 6750000 * 4;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
|
|
|
|
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
|
2010-07-09 00:12:47 +08:00
|
|
|
f->fmt.vbi.offset = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.flags = 0;
|
|
|
|
f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
|
2009-03-03 17:14:34 +08:00
|
|
|
PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
|
2009-03-03 17:14:34 +08:00
|
|
|
PAL_VBI_LINES : NTSC_VBI_LINES;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
|
2009-03-03 17:14:34 +08:00
|
|
|
PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
|
2009-03-04 01:37:50 +08:00
|
|
|
f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
|
2013-02-09 17:41:11 +08:00
|
|
|
memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-02-09 17:41:11 +08:00
|
|
|
static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
|
|
|
|
struct v4l2_format *f)
|
|
|
|
{
|
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
|
|
|
|
if (dev->vbi_stream_on && !fh->stream_on) {
|
|
|
|
cx231xx_errdev("%s device in use by another fh\n", __func__);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
return vidioc_try_fmt_vbi_cap(file, priv, f);
|
|
|
|
}
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
static int vidioc_reqbufs(struct file *file, void *priv,
|
|
|
|
struct v4l2_requestbuffers *rb)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
return videobuf_reqbufs(&fh->vb_vidq, rb);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
return videobuf_querybuf(&fh->vb_vidq, b);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
return videobuf_qbuf(&fh->vb_vidq, b);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = priv;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
/* RADIO ESPECIFIC IOCTLS */
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
|
|
|
|
|
2013-01-29 23:32:20 +08:00
|
|
|
if (t->index)
|
2009-03-04 01:37:50 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
strcpy(t->name, "Radio");
|
|
|
|
|
2013-01-29 23:32:20 +08:00
|
|
|
call_all(dev, tuner, g_tuner, t);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-03-15 17:10:06 +08:00
|
|
|
static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
|
|
|
|
|
2013-01-30 00:16:06 +08:00
|
|
|
if (t->index)
|
2009-03-04 01:37:50 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-21 10:33:48 +08:00
|
|
|
call_all(dev, tuner, s_tuner, t);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cx231xx_v4l2_open()
|
|
|
|
* inits the device and starts isoc transfer
|
|
|
|
*/
|
|
|
|
static int cx231xx_v4l2_open(struct file *filp)
|
|
|
|
{
|
|
|
|
int errCode = 0, radio = 0;
|
2009-12-10 21:44:04 +08:00
|
|
|
struct video_device *vdev = video_devdata(filp);
|
|
|
|
struct cx231xx *dev = video_drvdata(filp);
|
2009-03-04 01:37:50 +08:00
|
|
|
struct cx231xx_fh *fh;
|
|
|
|
enum v4l2_buf_type fh_type = 0;
|
|
|
|
|
2009-12-10 21:44:04 +08:00
|
|
|
switch (vdev->vfl_type) {
|
|
|
|
case VFL_TYPE_GRABBER:
|
|
|
|
fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
break;
|
|
|
|
case VFL_TYPE_VBI:
|
|
|
|
fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
|
|
|
|
break;
|
|
|
|
case VFL_TYPE_RADIO:
|
|
|
|
radio = 1;
|
|
|
|
break;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-12-10 21:47:13 +08:00
|
|
|
cx231xx_videodbg("open dev=%s type=%s users=%d\n",
|
|
|
|
video_device_node_name(vdev), v4l2_type_names[fh_type],
|
|
|
|
dev->users);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
|
|
|
|
if (errCode < 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_errdev
|
|
|
|
("Device locked on digital mode. Can't open analog\n");
|
2009-03-04 01:37:50 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
|
|
|
|
if (!fh) {
|
|
|
|
cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2012-06-24 17:43:02 +08:00
|
|
|
if (mutex_lock_interruptible(&dev->lock)) {
|
|
|
|
kfree(fh);
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
fh->dev = dev;
|
|
|
|
fh->type = fh_type;
|
|
|
|
filp->private_data = fh;
|
2012-09-17 18:31:04 +08:00
|
|
|
v4l2_fh_init(&fh->fh, vdev);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Power up in Analog TV mode */
|
2011-02-01 09:12:15 +08:00
|
|
|
if (dev->board.external_av)
|
2010-07-07 05:12:25 +08:00
|
|
|
cx231xx_set_power_mode(dev,
|
|
|
|
POLARIS_AVMODE_ENXTERNAL_AV);
|
|
|
|
else
|
|
|
|
cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
|
|
|
|
#endif
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set video alternate setting */
|
|
|
|
cx231xx_set_video_alternate(dev);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* Needed, since GPIO might have disabled power of
|
|
|
|
some i2c device */
|
|
|
|
cx231xx_config_i2c(dev);
|
|
|
|
|
|
|
|
/* device needs to be initialized before isoc transfer */
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
}
|
2013-01-28 23:57:47 +08:00
|
|
|
if (radio) {
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_videodbg("video_open: setting radio device\n");
|
|
|
|
|
|
|
|
/* cx231xx_start_radio(dev); */
|
|
|
|
|
2009-03-21 10:33:48 +08:00
|
|
|
call_all(dev, tuner, s_radio);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
dev->users++;
|
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
|
|
|
|
NULL, &dev->video_mode.slock,
|
|
|
|
fh->type, V4L2_FIELD_INTERLACED,
|
2010-09-21 04:39:46 +08:00
|
|
|
sizeof(struct cx231xx_buffer),
|
2010-10-10 00:13:35 +08:00
|
|
|
fh, &dev->lock);
|
2009-03-03 17:14:34 +08:00
|
|
|
if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
2009-03-04 00:31:36 +08:00
|
|
|
/* Set the required alternate setting VBI interface works in
|
|
|
|
Bulk mode only */
|
2011-02-01 09:12:15 +08:00
|
|
|
cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-04 00:31:36 +08:00
|
|
|
videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
|
|
|
|
NULL, &dev->vbi_mode.slock,
|
|
|
|
fh->type, V4L2_FIELD_SEQ_TB,
|
2010-09-21 04:39:46 +08:00
|
|
|
sizeof(struct cx231xx_buffer),
|
2010-10-10 00:13:35 +08:00
|
|
|
fh, &dev->lock);
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
2012-06-24 17:43:02 +08:00
|
|
|
mutex_unlock(&dev->lock);
|
2012-09-17 18:31:04 +08:00
|
|
|
v4l2_fh_add(&fh->fh);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cx231xx_realease_resources()
|
|
|
|
* unregisters the v4l2,i2c and usb devices
|
|
|
|
* called when the device gets disconected or at module unload
|
|
|
|
*/
|
|
|
|
void cx231xx_release_analog_resources(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*FIXME: I2C IR should be disconnected */
|
|
|
|
|
|
|
|
if (dev->radio_dev) {
|
2009-11-28 00:57:30 +08:00
|
|
|
if (video_is_registered(dev->radio_dev))
|
2009-03-04 01:37:50 +08:00
|
|
|
video_unregister_device(dev->radio_dev);
|
|
|
|
else
|
|
|
|
video_device_release(dev->radio_dev);
|
|
|
|
dev->radio_dev = NULL;
|
|
|
|
}
|
|
|
|
if (dev->vbi_dev) {
|
2009-11-28 00:57:15 +08:00
|
|
|
cx231xx_info("V4L2 device %s deregistered\n",
|
|
|
|
video_device_node_name(dev->vbi_dev));
|
2009-11-28 00:57:30 +08:00
|
|
|
if (video_is_registered(dev->vbi_dev))
|
2009-03-04 01:37:50 +08:00
|
|
|
video_unregister_device(dev->vbi_dev);
|
|
|
|
else
|
|
|
|
video_device_release(dev->vbi_dev);
|
|
|
|
dev->vbi_dev = NULL;
|
|
|
|
}
|
|
|
|
if (dev->vdev) {
|
2009-11-28 00:57:15 +08:00
|
|
|
cx231xx_info("V4L2 device %s deregistered\n",
|
|
|
|
video_device_node_name(dev->vdev));
|
2010-07-07 05:12:25 +08:00
|
|
|
|
2011-02-01 09:12:15 +08:00
|
|
|
if (dev->board.has_417)
|
2010-07-07 05:12:25 +08:00
|
|
|
cx231xx_417_unregister(dev);
|
|
|
|
|
2009-11-28 00:57:30 +08:00
|
|
|
if (video_is_registered(dev->vdev))
|
2009-03-04 01:37:50 +08:00
|
|
|
video_unregister_device(dev->vdev);
|
|
|
|
else
|
|
|
|
video_device_release(dev->vdev);
|
|
|
|
dev->vdev = NULL;
|
|
|
|
}
|
2012-09-17 18:22:09 +08:00
|
|
|
v4l2_ctrl_handler_free(&dev->ctrl_handler);
|
|
|
|
v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-06-24 17:43:02 +08:00
|
|
|
* cx231xx_close()
|
2009-03-04 01:37:50 +08:00
|
|
|
* stops streaming and deallocates all resources allocated by the v4l2
|
|
|
|
* calls and ioctls
|
|
|
|
*/
|
2012-06-24 17:43:02 +08:00
|
|
|
static int cx231xx_close(struct file *filp)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = filp->private_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
cx231xx_videodbg("users=%d\n", dev->users);
|
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
cx231xx_videodbg("users=%d\n", dev->users);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (res_check(fh))
|
|
|
|
res_free(fh);
|
|
|
|
|
2011-02-01 09:12:15 +08:00
|
|
|
/*
|
|
|
|
* To workaround error number=-71 on EP0 for VideoGrabber,
|
|
|
|
* need exclude following.
|
|
|
|
* FIXME: It is probably safe to remove most of these, as we're
|
|
|
|
* now avoiding the alternate setting for INDEX_VANC
|
|
|
|
*/
|
|
|
|
if (!dev->board.no_alt_vanc)
|
2010-07-07 05:12:25 +08:00
|
|
|
if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
|
|
|
videobuf_stop(&fh->vb_vidq);
|
|
|
|
videobuf_mmap_free(&fh->vb_vidq);
|
|
|
|
|
|
|
|
/* the device is already disconnect,
|
|
|
|
free the remaining resources */
|
|
|
|
if (dev->state & DEV_DISCONNECTED) {
|
|
|
|
if (atomic_read(&dev->devlist_count) > 0) {
|
|
|
|
cx231xx_release_resources(dev);
|
2012-03-05 03:25:04 +08:00
|
|
|
fh->dev = NULL;
|
2010-07-07 05:12:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2009-03-03 17:14:34 +08:00
|
|
|
|
2010-07-07 05:12:25 +08:00
|
|
|
/* do this before setting alternate! */
|
|
|
|
cx231xx_uninit_vbi_isoc(dev);
|
|
|
|
|
|
|
|
/* set alternate 0 */
|
|
|
|
if (!dev->vbi_or_sliced_cc_mode)
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
|
|
|
|
else
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
|
|
|
|
|
2012-09-17 18:31:04 +08:00
|
|
|
v4l2_fh_del(&fh->fh);
|
|
|
|
v4l2_fh_exit(&fh->fh);
|
2010-07-07 05:12:25 +08:00
|
|
|
kfree(fh);
|
|
|
|
dev->users--;
|
|
|
|
wake_up_interruptible_nr(&dev->open, 1);
|
2009-03-03 17:14:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-17 18:31:04 +08:00
|
|
|
v4l2_fh_del(&fh->fh);
|
2012-01-10 19:48:50 +08:00
|
|
|
dev->users--;
|
|
|
|
if (!dev->users) {
|
2009-03-04 01:37:50 +08:00
|
|
|
videobuf_stop(&fh->vb_vidq);
|
|
|
|
videobuf_mmap_free(&fh->vb_vidq);
|
|
|
|
|
|
|
|
/* the device is already disconnect,
|
|
|
|
free the remaining resources */
|
|
|
|
if (dev->state & DEV_DISCONNECTED) {
|
|
|
|
cx231xx_release_resources(dev);
|
2012-03-05 03:25:04 +08:00
|
|
|
fh->dev = NULL;
|
2009-03-04 01:37:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Save some power by putting tuner to sleep */
|
2009-10-05 21:48:17 +08:00
|
|
|
call_all(dev, core, s_power, 0);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* do this before setting alternate! */
|
2010-07-07 05:12:25 +08:00
|
|
|
if (dev->USE_ISO)
|
|
|
|
cx231xx_uninit_isoc(dev);
|
|
|
|
else
|
|
|
|
cx231xx_uninit_bulk(dev);
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
|
|
|
|
|
|
|
/* set alternate 0 */
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
|
|
|
|
}
|
2012-09-17 18:31:04 +08:00
|
|
|
v4l2_fh_exit(&fh->fh);
|
2009-03-04 01:37:50 +08:00
|
|
|
kfree(fh);
|
|
|
|
wake_up_interruptible_nr(&dev->open, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-24 17:43:02 +08:00
|
|
|
static int cx231xx_v4l2_close(struct file *filp)
|
|
|
|
{
|
|
|
|
struct cx231xx_fh *fh = filp->private_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
mutex_lock(&dev->lock);
|
|
|
|
rc = cx231xx_close(filp);
|
|
|
|
mutex_unlock(&dev->lock);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
/*
|
|
|
|
* cx231xx_v4l2_read()
|
|
|
|
* will allocate buffers when called for the first time
|
|
|
|
*/
|
|
|
|
static ssize_t
|
2009-03-04 00:31:36 +08:00
|
|
|
cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
|
|
|
|
loff_t *pos)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
struct cx231xx_fh *fh = filp->private_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
|
|
|
|
(fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
|
2009-03-04 01:37:50 +08:00
|
|
|
rc = res_get(fh);
|
|
|
|
|
|
|
|
if (unlikely(rc < 0))
|
|
|
|
return rc;
|
|
|
|
|
2012-06-24 17:43:02 +08:00
|
|
|
if (mutex_lock_interruptible(&dev->lock))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
|
2009-03-03 17:14:34 +08:00
|
|
|
filp->f_flags & O_NONBLOCK);
|
2012-06-24 17:43:02 +08:00
|
|
|
mutex_unlock(&dev->lock);
|
|
|
|
return rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cx231xx_v4l2_poll()
|
|
|
|
* will allocate buffers when called for the first time
|
|
|
|
*/
|
2010-07-07 05:12:25 +08:00
|
|
|
static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2012-09-17 18:31:04 +08:00
|
|
|
unsigned long req_events = poll_requested_events(wait);
|
2009-03-04 01:37:50 +08:00
|
|
|
struct cx231xx_fh *fh = filp->private_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
2012-09-17 18:31:04 +08:00
|
|
|
unsigned res = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
2012-09-17 18:31:04 +08:00
|
|
|
return POLLERR;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = res_get(fh);
|
|
|
|
|
|
|
|
if (unlikely(rc < 0))
|
|
|
|
return POLLERR;
|
|
|
|
|
2012-09-17 18:31:04 +08:00
|
|
|
if (v4l2_event_pending(&fh->fh))
|
|
|
|
res |= POLLPRI;
|
|
|
|
else
|
|
|
|
poll_wait(filp, &fh->fh.wait, wait);
|
|
|
|
|
|
|
|
if (!(req_events & (POLLIN | POLLRDNORM)))
|
|
|
|
return res;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) ||
|
2012-06-24 17:43:02 +08:00
|
|
|
(V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) {
|
|
|
|
mutex_lock(&dev->lock);
|
2012-09-17 18:31:04 +08:00
|
|
|
res |= videobuf_poll_stream(filp, &fh->vb_vidq, wait);
|
2012-06-24 17:43:02 +08:00
|
|
|
mutex_unlock(&dev->lock);
|
|
|
|
return res;
|
|
|
|
}
|
2012-09-17 18:31:04 +08:00
|
|
|
return res | POLLERR;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cx231xx_v4l2_mmap()
|
|
|
|
*/
|
|
|
|
static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_fh *fh = filp->private_data;
|
|
|
|
struct cx231xx *dev = fh->dev;
|
|
|
|
int rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
rc = check_dev(dev);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
rc = res_get(fh);
|
|
|
|
|
|
|
|
if (unlikely(rc < 0))
|
|
|
|
return rc;
|
|
|
|
|
2012-06-24 17:43:02 +08:00
|
|
|
if (mutex_lock_interruptible(&dev->lock))
|
|
|
|
return -ERESTARTSYS;
|
2009-03-04 01:37:50 +08:00
|
|
|
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
|
2012-06-24 17:43:02 +08:00
|
|
|
mutex_unlock(&dev->lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
(unsigned long)vma->vm_start,
|
|
|
|
(unsigned long)vma->vm_end -
|
|
|
|
(unsigned long)vma->vm_start, rc);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct v4l2_file_operations cx231xx_v4l_fops = {
|
2009-03-04 00:31:36 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = cx231xx_v4l2_open,
|
2009-03-03 17:14:34 +08:00
|
|
|
.release = cx231xx_v4l2_close,
|
2009-03-04 00:31:36 +08:00
|
|
|
.read = cx231xx_v4l2_read,
|
|
|
|
.poll = cx231xx_v4l2_poll,
|
|
|
|
.mmap = cx231xx_v4l2_mmap,
|
2010-10-10 00:13:35 +08:00
|
|
|
.unlocked_ioctl = video_ioctl2,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_ioctl_ops video_ioctl_ops = {
|
2013-01-29 23:52:33 +08:00
|
|
|
.vidioc_querycap = cx231xx_querycap,
|
2009-03-04 00:31:36 +08:00
|
|
|
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
|
|
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
|
|
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
|
|
.vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
|
|
|
|
.vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
|
2013-02-09 17:41:11 +08:00
|
|
|
.vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
|
2009-03-04 00:31:36 +08:00
|
|
|
.vidioc_cropcap = vidioc_cropcap,
|
|
|
|
.vidioc_reqbufs = vidioc_reqbufs,
|
|
|
|
.vidioc_querybuf = vidioc_querybuf,
|
|
|
|
.vidioc_qbuf = vidioc_qbuf,
|
|
|
|
.vidioc_dqbuf = vidioc_dqbuf,
|
|
|
|
.vidioc_s_std = vidioc_s_std,
|
|
|
|
.vidioc_g_std = vidioc_g_std,
|
2013-01-30 00:16:06 +08:00
|
|
|
.vidioc_enum_input = cx231xx_enum_input,
|
|
|
|
.vidioc_g_input = cx231xx_g_input,
|
|
|
|
.vidioc_s_input = cx231xx_s_input,
|
2009-03-04 00:31:36 +08:00
|
|
|
.vidioc_streamon = vidioc_streamon,
|
|
|
|
.vidioc_streamoff = vidioc_streamoff,
|
2013-01-30 00:16:06 +08:00
|
|
|
.vidioc_g_tuner = cx231xx_g_tuner,
|
|
|
|
.vidioc_s_tuner = cx231xx_s_tuner,
|
|
|
|
.vidioc_g_frequency = cx231xx_g_frequency,
|
|
|
|
.vidioc_s_frequency = cx231xx_s_frequency,
|
2009-03-04 01:37:50 +08:00
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
2013-05-29 17:59:43 +08:00
|
|
|
.vidioc_g_chip_info = cx231xx_g_chip_info,
|
2013-01-30 00:16:06 +08:00
|
|
|
.vidioc_g_register = cx231xx_g_register,
|
|
|
|
.vidioc_s_register = cx231xx_s_register,
|
2009-03-04 01:37:50 +08:00
|
|
|
#endif
|
2012-09-17 18:31:04 +08:00
|
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct video_device cx231xx_vbi_template;
|
|
|
|
|
|
|
|
static const struct video_device cx231xx_video_template = {
|
2009-03-04 00:31:36 +08:00
|
|
|
.fops = &cx231xx_v4l_fops,
|
|
|
|
.release = video_device_release,
|
|
|
|
.ioctl_ops = &video_ioctl_ops,
|
|
|
|
.tvnorms = V4L2_STD_ALL,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_file_operations radio_fops = {
|
2009-03-04 00:31:36 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = cx231xx_v4l2_open,
|
2009-03-03 17:14:34 +08:00
|
|
|
.release = cx231xx_v4l2_close,
|
2012-09-17 18:31:04 +08:00
|
|
|
.poll = v4l2_ctrl_poll,
|
2012-09-17 20:26:46 +08:00
|
|
|
.unlocked_ioctl = video_ioctl2,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct v4l2_ioctl_ops radio_ioctl_ops = {
|
2013-01-29 23:52:33 +08:00
|
|
|
.vidioc_querycap = cx231xx_querycap,
|
2009-03-04 00:31:36 +08:00
|
|
|
.vidioc_g_tuner = radio_g_tuner,
|
|
|
|
.vidioc_s_tuner = radio_s_tuner,
|
2013-01-30 00:16:06 +08:00
|
|
|
.vidioc_g_frequency = cx231xx_g_frequency,
|
|
|
|
.vidioc_s_frequency = cx231xx_s_frequency,
|
2009-03-04 01:37:50 +08:00
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
2013-05-29 17:59:43 +08:00
|
|
|
.vidioc_g_chip_info = cx231xx_g_chip_info,
|
2013-01-30 00:16:06 +08:00
|
|
|
.vidioc_g_register = cx231xx_g_register,
|
|
|
|
.vidioc_s_register = cx231xx_s_register,
|
2009-03-04 01:37:50 +08:00
|
|
|
#endif
|
2012-09-17 18:31:04 +08:00
|
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct video_device cx231xx_radio_template = {
|
2009-03-04 00:31:36 +08:00
|
|
|
.name = "cx231xx-radio",
|
|
|
|
.fops = &radio_fops,
|
2009-03-03 17:14:34 +08:00
|
|
|
.ioctl_ops = &radio_ioctl_ops,
|
2009-03-04 01:37:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************** usb interface ******************************/
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
|
|
|
|
const struct video_device
|
|
|
|
*template, const char *type_name)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
struct video_device *vfd;
|
|
|
|
|
|
|
|
vfd = video_device_alloc();
|
|
|
|
if (NULL == vfd)
|
|
|
|
return NULL;
|
2009-03-04 00:31:36 +08:00
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
*vfd = *template;
|
2009-03-21 10:33:48 +08:00
|
|
|
vfd->v4l2_dev = &dev->v4l2_dev;
|
2009-03-04 01:37:50 +08:00
|
|
|
vfd->release = video_device_release;
|
|
|
|
vfd->debug = video_debug;
|
2010-10-10 00:13:35 +08:00
|
|
|
vfd->lock = &dev->lock;
|
2012-09-17 18:31:04 +08:00
|
|
|
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-12-10 21:44:04 +08:00
|
|
|
video_set_drvdata(vfd, dev);
|
2012-09-13 17:17:47 +08:00
|
|
|
if (dev->tuner_type == TUNER_ABSENT) {
|
|
|
|
v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY);
|
|
|
|
v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY);
|
|
|
|
v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
|
|
|
|
v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
return vfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cx231xx_register_analog_devices(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
[media] Stop using linux/version.h on most video drivers
All the modified drivers didn't have any version increment since
Jan, 1 2011. Several of them didn't have any version increment
for a long time, even having new features and important bug fixes
happening.
As we're now filling the QUERYCAP version with the current Kernel
Release, we don't need to maintain a per-driver version control
anymore. So, let's just use the default.
In order to preserve the Kernel module version history, a
KERNEL_VERSION() macro were added to all modified drivers, and
the extraver number were incremented.
I opted to preserve the per-driver version control to a few
pwc, pvrusb2, s2255, s5p-fimc and sh_vou.
A few drivers are still using the legacy way to handle ioctl's.
So, we can't do such change on them, otherwise, they'll break.
Those are: uvc, et61x251 and sn9c102.
The rationale is that the per-driver version control seems to be
actively maintained on those.
Yet, I think that the better for them would be to just use the
default version numbering, instead of doing that by themselves.
While here, removed a few uneeded include linux/version.h
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-06-25 01:45:49 +08:00
|
|
|
cx231xx_info("%s: v4l2 driver version %s\n",
|
|
|
|
dev->name, CX231XX_VERSION);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* set default norm */
|
2012-09-17 19:30:07 +08:00
|
|
|
dev->norm = V4L2_STD_PAL;
|
2009-03-04 01:37:50 +08:00
|
|
|
dev->width = norm_maxw(dev);
|
|
|
|
dev->height = norm_maxh(dev);
|
|
|
|
dev->interlaced = 0;
|
|
|
|
|
|
|
|
/* Analog specific initialization */
|
|
|
|
dev->format = &format[0];
|
2010-07-13 02:34:57 +08:00
|
|
|
|
|
|
|
/* Set the initial input */
|
|
|
|
video_mux(dev, dev->video_input);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2013-01-29 21:50:54 +08:00
|
|
|
call_all(dev, core, s_std, dev->norm);
|
|
|
|
|
2012-09-17 18:22:09 +08:00
|
|
|
v4l2_ctrl_handler_init(&dev->ctrl_handler, 10);
|
|
|
|
v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5);
|
|
|
|
|
|
|
|
if (dev->sd_cx25840) {
|
|
|
|
v4l2_ctrl_add_handler(&dev->ctrl_handler,
|
|
|
|
dev->sd_cx25840->ctrl_handler, NULL);
|
|
|
|
v4l2_ctrl_add_handler(&dev->radio_ctrl_handler,
|
|
|
|
dev->sd_cx25840->ctrl_handler,
|
|
|
|
v4l2_ctrl_radio_filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->ctrl_handler.error)
|
|
|
|
return dev->ctrl_handler.error;
|
|
|
|
if (dev->radio_ctrl_handler.error)
|
|
|
|
return dev->radio_ctrl_handler.error;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* enable vbi capturing */
|
2009-03-03 17:14:34 +08:00
|
|
|
/* write code here... */
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* allocate and fill video video_device struct */
|
|
|
|
dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
|
|
|
|
if (!dev->vdev) {
|
|
|
|
cx231xx_errdev("cannot allocate video_device.\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2012-09-17 18:22:09 +08:00
|
|
|
dev->vdev->ctrl_handler = &dev->ctrl_handler;
|
2009-03-04 01:37:50 +08:00
|
|
|
/* register v4l2 video video_device */
|
|
|
|
ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
|
2009-03-03 17:14:34 +08:00
|
|
|
video_nr[dev->devno]);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (ret) {
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_errdev("unable to register video device (error=%i).\n",
|
|
|
|
ret);
|
2009-03-04 01:37:50 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-28 00:57:15 +08:00
|
|
|
cx231xx_info("%s/0: registered device %s [v4l2]\n",
|
|
|
|
dev->name, video_device_node_name(dev->vdev));
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Initialize VBI template */
|
2012-10-24 02:57:05 +08:00
|
|
|
cx231xx_vbi_template = cx231xx_video_template;
|
2009-03-03 17:14:34 +08:00
|
|
|
strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* Allocate and fill vbi video_device struct */
|
|
|
|
dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
|
|
|
|
|
2012-09-17 18:22:09 +08:00
|
|
|
if (!dev->vbi_dev) {
|
|
|
|
cx231xx_errdev("cannot allocate video_device.\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
dev->vbi_dev->ctrl_handler = &dev->ctrl_handler;
|
2009-03-04 01:37:50 +08:00
|
|
|
/* register v4l2 vbi video_device */
|
|
|
|
ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
|
2009-03-03 17:14:34 +08:00
|
|
|
vbi_nr[dev->devno]);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
cx231xx_errdev("unable to register vbi device\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-28 00:57:15 +08:00
|
|
|
cx231xx_info("%s/0: registered device %s\n",
|
|
|
|
dev->name, video_device_node_name(dev->vbi_dev));
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
|
2009-03-04 00:31:36 +08:00
|
|
|
dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
|
|
|
|
"radio");
|
2009-03-04 01:37:50 +08:00
|
|
|
if (!dev->radio_dev) {
|
|
|
|
cx231xx_errdev("cannot allocate video_device.\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2012-09-17 18:22:09 +08:00
|
|
|
dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler;
|
2009-03-04 01:37:50 +08:00
|
|
|
ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
|
|
|
|
radio_nr[dev->devno]);
|
|
|
|
if (ret < 0) {
|
|
|
|
cx231xx_errdev("can't register radio device\n");
|
|
|
|
return ret;
|
|
|
|
}
|
2009-11-28 00:57:15 +08:00
|
|
|
cx231xx_info("Registered radio device as %s\n",
|
|
|
|
video_device_node_name(dev->radio_dev));
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-11-28 00:57:15 +08:00
|
|
|
cx231xx_info("V4L2 device registered as %s and %s\n",
|
|
|
|
video_device_node_name(dev->vdev),
|
|
|
|
video_device_node_name(dev->vbi_dev));
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|