mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 06:41:43 +00:00
5a0e3ad6af
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>
2641 lines
72 KiB
C
2641 lines
72 KiB
C
/*
|
|
* usbvision-core.c - driver for NT100x USB video capture devices
|
|
*
|
|
*
|
|
* Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
|
|
* Dwaine Garden <dwainegarden@rogers.com>
|
|
*
|
|
* This module is part of usbvision driver project.
|
|
* Updates to driver completed by Dwaine P. Garden
|
|
*
|
|
* 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/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/spinlock.h>
|
|
#include <asm/io.h>
|
|
#include <linux/videodev2.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#include <media/saa7115.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/tuner.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include "usbvision.h"
|
|
|
|
static unsigned int core_debug;
|
|
module_param(core_debug,int,0644);
|
|
MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
|
|
|
|
static unsigned int force_testpattern;
|
|
module_param(force_testpattern,int,0644);
|
|
MODULE_PARM_DESC(force_testpattern,"enable test pattern display [core]");
|
|
|
|
static int adjustCompression = 1; /* Set the compression to be adaptive */
|
|
module_param(adjustCompression, int, 0444);
|
|
MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device. Default: 1 (On)");
|
|
|
|
/* To help people with Black and White output with using s-video input.
|
|
* Some cables and input device are wired differently. */
|
|
static int SwitchSVideoInput;
|
|
module_param(SwitchSVideoInput, int, 0444);
|
|
MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)");
|
|
|
|
static unsigned int adjust_X_Offset = -1;
|
|
module_param(adjust_X_Offset, int, 0644);
|
|
MODULE_PARM_DESC(adjust_X_Offset, "adjust X offset display [core]");
|
|
|
|
static unsigned int adjust_Y_Offset = -1;
|
|
module_param(adjust_Y_Offset, int, 0644);
|
|
MODULE_PARM_DESC(adjust_Y_Offset, "adjust Y offset display [core]");
|
|
|
|
|
|
#define ENABLE_HEXDUMP 0 /* Enable if you need it */
|
|
|
|
|
|
#ifdef USBVISION_DEBUG
|
|
#define PDEBUG(level, fmt, args...) { \
|
|
if (core_debug & (level)) \
|
|
printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
|
|
__func__, __LINE__ , ## args); \
|
|
}
|
|
#else
|
|
#define PDEBUG(level, fmt, args...) do {} while(0)
|
|
#endif
|
|
|
|
#define DBG_HEADER 1<<0
|
|
#define DBG_IRQ 1<<1
|
|
#define DBG_ISOC 1<<2
|
|
#define DBG_PARSE 1<<3
|
|
#define DBG_SCRATCH 1<<4
|
|
#define DBG_FUNC 1<<5
|
|
|
|
static const int max_imgwidth = MAX_FRAME_WIDTH;
|
|
static const int max_imgheight = MAX_FRAME_HEIGHT;
|
|
static const int min_imgwidth = MIN_FRAME_WIDTH;
|
|
static const int min_imgheight = MIN_FRAME_HEIGHT;
|
|
|
|
/* The value of 'scratch_buf_size' affects quality of the picture
|
|
* in many ways. Shorter buffers may cause loss of data when client
|
|
* is too slow. Larger buffers are memory-consuming and take longer
|
|
* to work with. This setting can be adjusted, but the default value
|
|
* should be OK for most desktop users.
|
|
*/
|
|
#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) // 128kB memory scratch buffer
|
|
static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE;
|
|
|
|
// Function prototypes
|
|
static int usbvision_request_intra (struct usb_usbvision *usbvision);
|
|
static int usbvision_unrequest_intra (struct usb_usbvision *usbvision);
|
|
static int usbvision_adjust_compression (struct usb_usbvision *usbvision);
|
|
static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision);
|
|
|
|
/*******************************/
|
|
/* Memory management functions */
|
|
/*******************************/
|
|
|
|
/*
|
|
* Here we want the physical address of the memory.
|
|
* This is used when initializing the contents of the area.
|
|
*/
|
|
|
|
static void *usbvision_rvmalloc(unsigned long size)
|
|
{
|
|
void *mem;
|
|
unsigned long adr;
|
|
|
|
size = PAGE_ALIGN(size);
|
|
mem = vmalloc_32(size);
|
|
if (!mem)
|
|
return NULL;
|
|
|
|
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
|
|
adr = (unsigned long) mem;
|
|
while (size > 0) {
|
|
SetPageReserved(vmalloc_to_page((void *)adr));
|
|
adr += PAGE_SIZE;
|
|
size -= PAGE_SIZE;
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
static void usbvision_rvfree(void *mem, unsigned long size)
|
|
{
|
|
unsigned long adr;
|
|
|
|
if (!mem)
|
|
return;
|
|
|
|
size = PAGE_ALIGN(size);
|
|
|
|
adr = (unsigned long) mem;
|
|
while ((long) size > 0) {
|
|
ClearPageReserved(vmalloc_to_page((void *)adr));
|
|
adr += PAGE_SIZE;
|
|
size -= PAGE_SIZE;
|
|
}
|
|
|
|
vfree(mem);
|
|
}
|
|
|
|
|
|
#if ENABLE_HEXDUMP
|
|
static void usbvision_hexdump(const unsigned char *data, int len)
|
|
{
|
|
char tmp[80];
|
|
int i, k;
|
|
|
|
for (i = k = 0; len > 0; i++, len--) {
|
|
if (i > 0 && (i % 16 == 0)) {
|
|
printk("%s\n", tmp);
|
|
k = 0;
|
|
}
|
|
k += sprintf(&tmp[k], "%02x ", data[i]);
|
|
}
|
|
if (k > 0)
|
|
printk("%s\n", tmp);
|
|
}
|
|
#endif
|
|
|
|
/********************************
|
|
* scratch ring buffer handling
|
|
********************************/
|
|
static int scratch_len(struct usb_usbvision *usbvision) /*This returns the amount of data actually in the buffer */
|
|
{
|
|
int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr;
|
|
if (len < 0) {
|
|
len += scratch_buf_size;
|
|
}
|
|
PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/* This returns the free space left in the buffer */
|
|
static int scratch_free(struct usb_usbvision *usbvision)
|
|
{
|
|
int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr;
|
|
if (free <= 0) {
|
|
free += scratch_buf_size;
|
|
}
|
|
if (free) {
|
|
free -= 1; /* at least one byte in the buffer must */
|
|
/* left blank, otherwise there is no chance to differ between full and empty */
|
|
}
|
|
PDEBUG(DBG_SCRATCH, "return %d\n", free);
|
|
|
|
return free;
|
|
}
|
|
|
|
|
|
/* This puts data into the buffer */
|
|
static int scratch_put(struct usb_usbvision *usbvision, unsigned char *data,
|
|
int len)
|
|
{
|
|
int len_part;
|
|
|
|
if (usbvision->scratch_write_ptr + len < scratch_buf_size) {
|
|
memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len);
|
|
usbvision->scratch_write_ptr += len;
|
|
}
|
|
else {
|
|
len_part = scratch_buf_size - usbvision->scratch_write_ptr;
|
|
memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part);
|
|
if (len == len_part) {
|
|
usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */
|
|
}
|
|
else {
|
|
memcpy(usbvision->scratch, data + len_part, len - len_part);
|
|
usbvision->scratch_write_ptr = len - len_part;
|
|
}
|
|
}
|
|
|
|
PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* This marks the write_ptr as position of new frame header */
|
|
static void scratch_mark_header(struct usb_usbvision *usbvision)
|
|
{
|
|
PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr);
|
|
|
|
usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] =
|
|
usbvision->scratch_write_ptr;
|
|
usbvision->scratch_headermarker_write_ptr += 1;
|
|
usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER;
|
|
}
|
|
|
|
/* This gets data from the buffer at the given "ptr" position */
|
|
static int scratch_get_extra(struct usb_usbvision *usbvision,
|
|
unsigned char *data, int *ptr, int len)
|
|
{
|
|
int len_part;
|
|
if (*ptr + len < scratch_buf_size) {
|
|
memcpy(data, usbvision->scratch + *ptr, len);
|
|
*ptr += len;
|
|
}
|
|
else {
|
|
len_part = scratch_buf_size - *ptr;
|
|
memcpy(data, usbvision->scratch + *ptr, len_part);
|
|
if (len == len_part) {
|
|
*ptr = 0; /* just set the y_ptr to zero */
|
|
}
|
|
else {
|
|
memcpy(data + len_part, usbvision->scratch, len - len_part);
|
|
*ptr = len - len_part;
|
|
}
|
|
}
|
|
|
|
PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/* This sets the scratch extra read pointer */
|
|
static void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr,
|
|
int len)
|
|
{
|
|
*ptr = (usbvision->scratch_read_ptr + len)%scratch_buf_size;
|
|
|
|
PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
|
|
}
|
|
|
|
|
|
/*This increments the scratch extra read pointer */
|
|
static void scratch_inc_extra_ptr(int *ptr, int len)
|
|
{
|
|
*ptr = (*ptr + len) % scratch_buf_size;
|
|
|
|
PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
|
|
}
|
|
|
|
|
|
/* This gets data from the buffer */
|
|
static int scratch_get(struct usb_usbvision *usbvision, unsigned char *data,
|
|
int len)
|
|
{
|
|
int len_part;
|
|
if (usbvision->scratch_read_ptr + len < scratch_buf_size) {
|
|
memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len);
|
|
usbvision->scratch_read_ptr += len;
|
|
}
|
|
else {
|
|
len_part = scratch_buf_size - usbvision->scratch_read_ptr;
|
|
memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part);
|
|
if (len == len_part) {
|
|
usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */
|
|
}
|
|
else {
|
|
memcpy(data + len_part, usbvision->scratch, len - len_part);
|
|
usbvision->scratch_read_ptr = len - len_part;
|
|
}
|
|
}
|
|
|
|
PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/* This sets read pointer to next header and returns it */
|
|
static int scratch_get_header(struct usb_usbvision *usbvision,
|
|
struct usbvision_frame_header *header)
|
|
{
|
|
int errCode = 0;
|
|
|
|
PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr);
|
|
|
|
while (usbvision->scratch_headermarker_write_ptr -
|
|
usbvision->scratch_headermarker_read_ptr != 0) {
|
|
usbvision->scratch_read_ptr =
|
|
usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr];
|
|
usbvision->scratch_headermarker_read_ptr += 1;
|
|
usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER;
|
|
scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH);
|
|
if ((header->magic_1 == USBVISION_MAGIC_1)
|
|
&& (header->magic_2 == USBVISION_MAGIC_2)
|
|
&& (header->headerLength == USBVISION_HEADER_LENGTH)) {
|
|
errCode = USBVISION_HEADER_LENGTH;
|
|
header->frameWidth = header->frameWidthLo + (header->frameWidthHi << 8);
|
|
header->frameHeight = header->frameHeightLo + (header->frameHeightHi << 8);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/*This removes len bytes of old data from the buffer */
|
|
static void scratch_rm_old(struct usb_usbvision *usbvision, int len)
|
|
{
|
|
|
|
usbvision->scratch_read_ptr += len;
|
|
usbvision->scratch_read_ptr %= scratch_buf_size;
|
|
PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr);
|
|
}
|
|
|
|
|
|
/*This resets the buffer - kills all data in it too */
|
|
static void scratch_reset(struct usb_usbvision *usbvision)
|
|
{
|
|
PDEBUG(DBG_SCRATCH, "\n");
|
|
|
|
usbvision->scratch_read_ptr = 0;
|
|
usbvision->scratch_write_ptr = 0;
|
|
usbvision->scratch_headermarker_read_ptr = 0;
|
|
usbvision->scratch_headermarker_write_ptr = 0;
|
|
usbvision->isocstate = IsocState_NoFrame;
|
|
}
|
|
|
|
int usbvision_scratch_alloc(struct usb_usbvision *usbvision)
|
|
{
|
|
usbvision->scratch = vmalloc_32(scratch_buf_size);
|
|
scratch_reset(usbvision);
|
|
if(usbvision->scratch == NULL) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: unable to allocate %d bytes for scratch\n",
|
|
__func__, scratch_buf_size);
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void usbvision_scratch_free(struct usb_usbvision *usbvision)
|
|
{
|
|
vfree(usbvision->scratch);
|
|
usbvision->scratch = NULL;
|
|
|
|
}
|
|
|
|
/*
|
|
* usbvision_testpattern()
|
|
*
|
|
* Procedure forms a test pattern (yellow grid on blue background).
|
|
*
|
|
* Parameters:
|
|
* fullframe: if TRUE then entire frame is filled, otherwise the procedure
|
|
* continues from the current scanline.
|
|
* pmode 0: fill the frame with solid blue color (like on VCR or TV)
|
|
* 1: Draw a colored grid
|
|
*
|
|
*/
|
|
static void usbvision_testpattern(struct usb_usbvision *usbvision,
|
|
int fullframe, int pmode)
|
|
{
|
|
static const char proc[] = "usbvision_testpattern";
|
|
struct usbvision_frame *frame;
|
|
unsigned char *f;
|
|
int num_cell = 0;
|
|
int scan_length = 0;
|
|
static int num_pass;
|
|
|
|
if (usbvision == NULL) {
|
|
printk(KERN_ERR "%s: usbvision == NULL\n", proc);
|
|
return;
|
|
}
|
|
if (usbvision->curFrame == NULL) {
|
|
printk(KERN_ERR "%s: usbvision->curFrame is NULL.\n", proc);
|
|
return;
|
|
}
|
|
|
|
/* Grab the current frame */
|
|
frame = usbvision->curFrame;
|
|
|
|
/* Optionally start at the beginning */
|
|
if (fullframe) {
|
|
frame->curline = 0;
|
|
frame->scanlength = 0;
|
|
}
|
|
|
|
/* Form every scan line */
|
|
for (; frame->curline < frame->frmheight; frame->curline++) {
|
|
int i;
|
|
|
|
f = frame->data + (usbvision->curwidth * 3 * frame->curline);
|
|
for (i = 0; i < usbvision->curwidth; i++) {
|
|
unsigned char cb = 0x80;
|
|
unsigned char cg = 0;
|
|
unsigned char cr = 0;
|
|
|
|
if (pmode == 1) {
|
|
if (frame->curline % 32 == 0)
|
|
cb = 0, cg = cr = 0xFF;
|
|
else if (i % 32 == 0) {
|
|
if (frame->curline % 32 == 1)
|
|
num_cell++;
|
|
cb = 0, cg = cr = 0xFF;
|
|
} else {
|
|
cb =
|
|
((num_cell * 7) +
|
|
num_pass) & 0xFF;
|
|
cg =
|
|
((num_cell * 5) +
|
|
num_pass * 2) & 0xFF;
|
|
cr =
|
|
((num_cell * 3) +
|
|
num_pass * 3) & 0xFF;
|
|
}
|
|
} else {
|
|
/* Just the blue screen */
|
|
}
|
|
|
|
*f++ = cb;
|
|
*f++ = cg;
|
|
*f++ = cr;
|
|
scan_length += 3;
|
|
}
|
|
}
|
|
|
|
frame->grabstate = FrameState_Done;
|
|
frame->scanlength += scan_length;
|
|
++num_pass;
|
|
|
|
}
|
|
|
|
/*
|
|
* usbvision_decompress_alloc()
|
|
*
|
|
* allocates intermediate buffer for decompression
|
|
*/
|
|
int usbvision_decompress_alloc(struct usb_usbvision *usbvision)
|
|
{
|
|
int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2;
|
|
usbvision->IntraFrameBuffer = vmalloc_32(IFB_size);
|
|
if (usbvision->IntraFrameBuffer == NULL) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: unable to allocate %d for compr. frame buffer\n",
|
|
__func__, IFB_size);
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* usbvision_decompress_free()
|
|
*
|
|
* frees intermediate buffer for decompression
|
|
*/
|
|
void usbvision_decompress_free(struct usb_usbvision *usbvision)
|
|
{
|
|
vfree(usbvision->IntraFrameBuffer);
|
|
usbvision->IntraFrameBuffer = NULL;
|
|
|
|
}
|
|
|
|
/************************************************************
|
|
* Here comes the data parsing stuff that is run as interrupt
|
|
************************************************************/
|
|
/*
|
|
* usbvision_find_header()
|
|
*
|
|
* Locate one of supported header markers in the scratch buffer.
|
|
*/
|
|
static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision)
|
|
{
|
|
struct usbvision_frame *frame;
|
|
int foundHeader = 0;
|
|
|
|
frame = usbvision->curFrame;
|
|
|
|
while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) {
|
|
// found header in scratch
|
|
PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u",
|
|
frame->isocHeader.magic_2,
|
|
frame->isocHeader.magic_1,
|
|
frame->isocHeader.headerLength,
|
|
frame->isocHeader.frameNum,
|
|
frame->isocHeader.framePhase,
|
|
frame->isocHeader.frameLatency,
|
|
frame->isocHeader.dataFormat,
|
|
frame->isocHeader.formatParam,
|
|
frame->isocHeader.frameWidth,
|
|
frame->isocHeader.frameHeight);
|
|
|
|
if (usbvision->requestIntra) {
|
|
if (frame->isocHeader.formatParam & 0x80) {
|
|
foundHeader = 1;
|
|
usbvision->lastIsocFrameNum = -1; // do not check for lost frames this time
|
|
usbvision_unrequest_intra(usbvision);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
foundHeader = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundHeader) {
|
|
frame->frmwidth = frame->isocHeader.frameWidth * usbvision->stretch_width;
|
|
frame->frmheight = frame->isocHeader.frameHeight * usbvision->stretch_height;
|
|
frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth)>> 3;
|
|
}
|
|
else { // no header found
|
|
PDEBUG(DBG_HEADER, "skipping scratch data, no header");
|
|
scratch_reset(usbvision);
|
|
return ParseState_EndParse;
|
|
}
|
|
|
|
// found header
|
|
if (frame->isocHeader.dataFormat==ISOC_MODE_COMPRESS) {
|
|
//check isocHeader.frameNum for lost frames
|
|
if (usbvision->lastIsocFrameNum >= 0) {
|
|
if (((usbvision->lastIsocFrameNum + 1) % 32) != frame->isocHeader.frameNum) {
|
|
// unexpected frame drop: need to request new intra frame
|
|
PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isocHeader.frameNum);
|
|
usbvision_request_intra(usbvision);
|
|
return ParseState_NextFrame;
|
|
}
|
|
}
|
|
usbvision->lastIsocFrameNum = frame->isocHeader.frameNum;
|
|
}
|
|
usbvision->header_count++;
|
|
frame->scanstate = ScanState_Lines;
|
|
frame->curline = 0;
|
|
|
|
if (force_testpattern) {
|
|
usbvision_testpattern(usbvision, 1, 1);
|
|
return ParseState_NextFrame;
|
|
}
|
|
return ParseState_Continue;
|
|
}
|
|
|
|
static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision,
|
|
long *pcopylen)
|
|
{
|
|
volatile struct usbvision_frame *frame;
|
|
unsigned char *f;
|
|
int len;
|
|
int i;
|
|
unsigned char yuyv[4]={180, 128, 10, 128}; // YUV components
|
|
unsigned char rv, gv, bv; // RGB components
|
|
int clipmask_index, bytes_per_pixel;
|
|
int stretch_bytes, clipmask_add;
|
|
|
|
frame = usbvision->curFrame;
|
|
f = frame->data + (frame->v4l2_linesize * frame->curline);
|
|
|
|
/* Make sure there's enough data for the entire line */
|
|
len = (frame->isocHeader.frameWidth * 2)+5;
|
|
if (scratch_len(usbvision) < len) {
|
|
PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len);
|
|
return ParseState_Out;
|
|
}
|
|
|
|
if ((frame->curline + 1) >= frame->frmheight) {
|
|
return ParseState_NextFrame;
|
|
}
|
|
|
|
bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
|
|
stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
|
|
clipmask_index = frame->curline * MAX_FRAME_WIDTH;
|
|
clipmask_add = usbvision->stretch_width;
|
|
|
|
for (i = 0; i < frame->frmwidth; i+=(2 * usbvision->stretch_width)) {
|
|
|
|
scratch_get(usbvision, &yuyv[0], 4);
|
|
|
|
if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f++ = yuyv[0]; // Y
|
|
*f++ = yuyv[3]; // U
|
|
}
|
|
else {
|
|
|
|
YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv);
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_RGB565:
|
|
*f++ = (0x1F & rv) |
|
|
(0xE0 & (gv << 5));
|
|
*f++ = (0x07 & (gv >> 3)) |
|
|
(0xF8 & bv);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f++ = rv;
|
|
*f++ = gv;
|
|
*f++ = bv;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f++ = rv;
|
|
*f++ = gv;
|
|
*f++ = bv;
|
|
f++;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
*f++ = (0x1F & rv) |
|
|
(0xE0 & (gv << 5));
|
|
*f++ = (0x03 & (gv >> 3)) |
|
|
(0x7C & (bv << 2));
|
|
break;
|
|
}
|
|
}
|
|
clipmask_index += clipmask_add;
|
|
f += stretch_bytes;
|
|
|
|
if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f++ = yuyv[2]; // Y
|
|
*f++ = yuyv[1]; // V
|
|
}
|
|
else {
|
|
|
|
YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv);
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_RGB565:
|
|
*f++ = (0x1F & rv) |
|
|
(0xE0 & (gv << 5));
|
|
*f++ = (0x07 & (gv >> 3)) |
|
|
(0xF8 & bv);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f++ = rv;
|
|
*f++ = gv;
|
|
*f++ = bv;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f++ = rv;
|
|
*f++ = gv;
|
|
*f++ = bv;
|
|
f++;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
*f++ = (0x1F & rv) |
|
|
(0xE0 & (gv << 5));
|
|
*f++ = (0x03 & (gv >> 3)) |
|
|
(0x7C & (bv << 2));
|
|
break;
|
|
}
|
|
}
|
|
clipmask_index += clipmask_add;
|
|
f += stretch_bytes;
|
|
}
|
|
|
|
frame->curline += usbvision->stretch_height;
|
|
*pcopylen += frame->v4l2_linesize * usbvision->stretch_height;
|
|
|
|
if (frame->curline >= frame->frmheight) {
|
|
return ParseState_NextFrame;
|
|
}
|
|
else {
|
|
return ParseState_Continue;
|
|
}
|
|
}
|
|
|
|
/* The decompression routine */
|
|
static int usbvision_decompress(struct usb_usbvision *usbvision,unsigned char *Compressed,
|
|
unsigned char *Decompressed, int *StartPos,
|
|
int *BlockTypeStartPos, int Len)
|
|
{
|
|
int RestPixel, Idx, MaxPos, Pos, ExtraPos, BlockLen, BlockTypePos, BlockTypeLen;
|
|
unsigned char BlockByte, BlockCode, BlockType, BlockTypeByte, Integrator;
|
|
|
|
Integrator = 0;
|
|
Pos = *StartPos;
|
|
BlockTypePos = *BlockTypeStartPos;
|
|
MaxPos = 396; //Pos + Len;
|
|
ExtraPos = Pos;
|
|
BlockLen = 0;
|
|
BlockByte = 0;
|
|
BlockCode = 0;
|
|
BlockType = 0;
|
|
BlockTypeByte = 0;
|
|
BlockTypeLen = 0;
|
|
RestPixel = Len;
|
|
|
|
for (Idx = 0; Idx < Len; Idx++) {
|
|
|
|
if (BlockLen == 0) {
|
|
if (BlockTypeLen==0) {
|
|
BlockTypeByte = Compressed[BlockTypePos];
|
|
BlockTypePos++;
|
|
BlockTypeLen = 4;
|
|
}
|
|
BlockType = (BlockTypeByte & 0xC0) >> 6;
|
|
|
|
//statistic:
|
|
usbvision->ComprBlockTypes[BlockType]++;
|
|
|
|
Pos = ExtraPos;
|
|
if (BlockType == 0) {
|
|
if(RestPixel >= 24) {
|
|
Idx += 23;
|
|
RestPixel -= 24;
|
|
Integrator = Decompressed[Idx];
|
|
} else {
|
|
Idx += RestPixel - 1;
|
|
RestPixel = 0;
|
|
}
|
|
} else {
|
|
BlockCode = Compressed[Pos];
|
|
Pos++;
|
|
if (RestPixel >= 24) {
|
|
BlockLen = 24;
|
|
} else {
|
|
BlockLen = RestPixel;
|
|
}
|
|
RestPixel -= BlockLen;
|
|
ExtraPos = Pos + (BlockLen / 4);
|
|
}
|
|
BlockTypeByte <<= 2;
|
|
BlockTypeLen -= 1;
|
|
}
|
|
if (BlockLen > 0) {
|
|
if ((BlockLen%4) == 0) {
|
|
BlockByte = Compressed[Pos];
|
|
Pos++;
|
|
}
|
|
if (BlockType == 1) { //inter Block
|
|
Integrator = Decompressed[Idx];
|
|
}
|
|
switch (BlockByte & 0xC0) {
|
|
case 0x03<<6:
|
|
Integrator += Compressed[ExtraPos];
|
|
ExtraPos++;
|
|
break;
|
|
case 0x02<<6:
|
|
Integrator += BlockCode;
|
|
break;
|
|
case 0x00:
|
|
Integrator -= BlockCode;
|
|
break;
|
|
}
|
|
Decompressed[Idx] = Integrator;
|
|
BlockByte <<= 2;
|
|
BlockLen -= 1;
|
|
}
|
|
}
|
|
*StartPos = ExtraPos;
|
|
*BlockTypeStartPos = BlockTypePos;
|
|
return Idx;
|
|
}
|
|
|
|
|
|
/*
|
|
* usbvision_parse_compress()
|
|
*
|
|
* Parse compressed frame from the scratch buffer, put
|
|
* decoded RGB value into the current frame buffer and add the written
|
|
* number of bytes (RGB) to the *pcopylen.
|
|
*
|
|
*/
|
|
static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision,
|
|
long *pcopylen)
|
|
{
|
|
#define USBVISION_STRIP_MAGIC 0x5A
|
|
#define USBVISION_STRIP_LEN_MAX 400
|
|
#define USBVISION_STRIP_HEADER_LEN 3
|
|
|
|
struct usbvision_frame *frame;
|
|
unsigned char *f,*u = NULL ,*v = NULL;
|
|
unsigned char StripData[USBVISION_STRIP_LEN_MAX];
|
|
unsigned char StripHeader[USBVISION_STRIP_HEADER_LEN];
|
|
int Idx, IdxEnd, StripLen, StripPtr, StartBlockPos, BlockPos, BlockTypePos;
|
|
int clipmask_index, bytes_per_pixel, rc;
|
|
int imageSize;
|
|
unsigned char rv, gv, bv;
|
|
static unsigned char *Y, *U, *V;
|
|
|
|
frame = usbvision->curFrame;
|
|
imageSize = frame->frmwidth * frame->frmheight;
|
|
if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) ||
|
|
(frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) { // this is a planar format
|
|
//... v4l2_linesize not used here.
|
|
f = frame->data + (frame->width * frame->curline);
|
|
} else
|
|
f = frame->data + (frame->v4l2_linesize * frame->curline);
|
|
|
|
if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers
|
|
// get base of u and b planes add halfoffset
|
|
|
|
u = frame->data
|
|
+ imageSize
|
|
+ (frame->frmwidth >>1) * frame->curline ;
|
|
v = u + (imageSize >>1 );
|
|
|
|
} else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){
|
|
|
|
v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ;
|
|
u = v + (imageSize >>2) ;
|
|
}
|
|
|
|
if (frame->curline == 0) {
|
|
usbvision_adjust_compression(usbvision);
|
|
}
|
|
|
|
if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) {
|
|
return ParseState_Out;
|
|
}
|
|
|
|
//get strip header without changing the scratch_read_ptr
|
|
scratch_set_extra_ptr(usbvision, &StripPtr, 0);
|
|
scratch_get_extra(usbvision, &StripHeader[0], &StripPtr,
|
|
USBVISION_STRIP_HEADER_LEN);
|
|
|
|
if (StripHeader[0] != USBVISION_STRIP_MAGIC) {
|
|
// wrong strip magic
|
|
usbvision->stripMagicErrors++;
|
|
return ParseState_NextFrame;
|
|
}
|
|
|
|
if (frame->curline != (int)StripHeader[2]) {
|
|
//line number missmatch error
|
|
usbvision->stripLineNumberErrors++;
|
|
}
|
|
|
|
StripLen = 2 * (unsigned int)StripHeader[1];
|
|
if (StripLen > USBVISION_STRIP_LEN_MAX) {
|
|
// strip overrun
|
|
// I think this never happens
|
|
usbvision_request_intra(usbvision);
|
|
}
|
|
|
|
if (scratch_len(usbvision) < StripLen) {
|
|
//there is not enough data for the strip
|
|
return ParseState_Out;
|
|
}
|
|
|
|
if (usbvision->IntraFrameBuffer) {
|
|
Y = usbvision->IntraFrameBuffer + frame->frmwidth * frame->curline;
|
|
U = usbvision->IntraFrameBuffer + imageSize + (frame->frmwidth / 2) * (frame->curline / 2);
|
|
V = usbvision->IntraFrameBuffer + imageSize / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2);
|
|
}
|
|
else {
|
|
return ParseState_NextFrame;
|
|
}
|
|
|
|
bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
|
|
clipmask_index = frame->curline * MAX_FRAME_WIDTH;
|
|
|
|
scratch_get(usbvision, StripData, StripLen);
|
|
|
|
IdxEnd = frame->frmwidth;
|
|
BlockTypePos = USBVISION_STRIP_HEADER_LEN;
|
|
StartBlockPos = BlockTypePos + (IdxEnd - 1) / 96 + (IdxEnd / 2 - 1) / 96 + 2;
|
|
BlockPos = StartBlockPos;
|
|
|
|
usbvision->BlockPos = BlockPos;
|
|
|
|
if ((rc = usbvision_decompress(usbvision, StripData, Y, &BlockPos, &BlockTypePos, IdxEnd)) != IdxEnd) {
|
|
//return ParseState_Continue;
|
|
}
|
|
if (StripLen > usbvision->maxStripLen) {
|
|
usbvision->maxStripLen = StripLen;
|
|
}
|
|
|
|
if (frame->curline%2) {
|
|
if ((rc = usbvision_decompress(usbvision, StripData, V, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
|
|
//return ParseState_Continue;
|
|
}
|
|
}
|
|
else {
|
|
if ((rc = usbvision_decompress(usbvision, StripData, U, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
|
|
//return ParseState_Continue;
|
|
}
|
|
}
|
|
|
|
if (BlockPos > usbvision->comprBlockPos) {
|
|
usbvision->comprBlockPos = BlockPos;
|
|
}
|
|
if (BlockPos > StripLen) {
|
|
usbvision->stripLenErrors++;
|
|
}
|
|
|
|
for (Idx = 0; Idx < IdxEnd; Idx++) {
|
|
if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f++ = Y[Idx];
|
|
*f++ = Idx & 0x01 ? U[Idx/2] : V[Idx/2];
|
|
}
|
|
else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) {
|
|
*f++ = Y[Idx];
|
|
if ( Idx & 0x01)
|
|
*u++ = U[Idx>>1] ;
|
|
else
|
|
*v++ = V[Idx>>1];
|
|
}
|
|
else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) {
|
|
*f++ = Y [Idx];
|
|
if ( !(( Idx & 0x01 ) | ( frame->curline & 0x01 )) ){
|
|
|
|
/* only need do this for 1 in 4 pixels */
|
|
/* intraframe buffer is YUV420 format */
|
|
|
|
*u++ = U[Idx >>1];
|
|
*v++ = V[Idx >>1];
|
|
}
|
|
|
|
}
|
|
else {
|
|
YUV_TO_RGB_BY_THE_BOOK(Y[Idx], U[Idx/2], V[Idx/2], rv, gv, bv);
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_GREY:
|
|
*f++ = Y[Idx];
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
*f++ = (0x1F & rv) |
|
|
(0xE0 & (gv << 5));
|
|
*f++ = (0x03 & (gv >> 3)) |
|
|
(0x7C & (bv << 2));
|
|
break;
|
|
case V4L2_PIX_FMT_RGB565:
|
|
*f++ = (0x1F & rv) |
|
|
(0xE0 & (gv << 5));
|
|
*f++ = (0x07 & (gv >> 3)) |
|
|
(0xF8 & bv);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f++ = rv;
|
|
*f++ = gv;
|
|
*f++ = bv;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f++ = rv;
|
|
*f++ = gv;
|
|
*f++ = bv;
|
|
f++;
|
|
break;
|
|
}
|
|
}
|
|
clipmask_index++;
|
|
}
|
|
/* Deal with non-integer no. of bytes for YUV420P */
|
|
if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 )
|
|
*pcopylen += frame->v4l2_linesize;
|
|
else
|
|
*pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1;
|
|
|
|
frame->curline += 1;
|
|
|
|
if (frame->curline >= frame->frmheight) {
|
|
return ParseState_NextFrame;
|
|
}
|
|
else {
|
|
return ParseState_Continue;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* usbvision_parse_lines_420()
|
|
*
|
|
* Parse two lines from the scratch buffer, put
|
|
* decoded RGB value into the current frame buffer and add the written
|
|
* number of bytes (RGB) to the *pcopylen.
|
|
*
|
|
*/
|
|
static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision,
|
|
long *pcopylen)
|
|
{
|
|
struct usbvision_frame *frame;
|
|
unsigned char *f_even = NULL, *f_odd = NULL;
|
|
unsigned int pixel_per_line, block;
|
|
int pixel, block_split;
|
|
int y_ptr, u_ptr, v_ptr, y_odd_offset;
|
|
const int y_block_size = 128;
|
|
const int uv_block_size = 64;
|
|
const int sub_block_size = 32;
|
|
const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4;
|
|
const int uv_step[]= { 0, 0, 0, 4 }, uv_step_size = 4;
|
|
unsigned char y[2], u, v; /* YUV components */
|
|
int y_, u_, v_, vb, uvg, ur;
|
|
int r_, g_, b_; /* RGB components */
|
|
unsigned char g;
|
|
int clipmask_even_index, clipmask_odd_index, bytes_per_pixel;
|
|
int clipmask_add, stretch_bytes;
|
|
|
|
frame = usbvision->curFrame;
|
|
f_even = frame->data + (frame->v4l2_linesize * frame->curline);
|
|
f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height;
|
|
|
|
/* Make sure there's enough data for the entire line */
|
|
/* In this mode usbvision transfer 3 bytes for every 2 pixels */
|
|
/* I need two lines to decode the color */
|
|
bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
|
|
stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
|
|
clipmask_even_index = frame->curline * MAX_FRAME_WIDTH;
|
|
clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH;
|
|
clipmask_add = usbvision->stretch_width;
|
|
pixel_per_line = frame->isocHeader.frameWidth;
|
|
|
|
if (scratch_len(usbvision) < (int)pixel_per_line * 3) {
|
|
//printk(KERN_DEBUG "out of data, need %d\n", len);
|
|
return ParseState_Out;
|
|
}
|
|
|
|
if ((frame->curline + 1) >= frame->frmheight) {
|
|
return ParseState_NextFrame;
|
|
}
|
|
|
|
block_split = (pixel_per_line%y_block_size) ? 1 : 0; //are some blocks splitted into different lines?
|
|
|
|
y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size)
|
|
+ block_split * uv_block_size;
|
|
|
|
scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset);
|
|
scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size);
|
|
scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset
|
|
+ (4 - block_split) * sub_block_size);
|
|
|
|
for (block = 0; block < (pixel_per_line / sub_block_size);
|
|
block++) {
|
|
|
|
|
|
for (pixel = 0; pixel < sub_block_size; pixel +=2) {
|
|
scratch_get(usbvision, &y[0], 2);
|
|
scratch_get_extra(usbvision, &u, &u_ptr, 1);
|
|
scratch_get_extra(usbvision, &v, &v_ptr, 1);
|
|
|
|
//I don't use the YUV_TO_RGB macro for better performance
|
|
v_ = v - 128;
|
|
u_ = u - 128;
|
|
vb = 132252 * v_;
|
|
uvg= -53281 * u_ - 25625 * v_;
|
|
ur = 104595 * u_;
|
|
|
|
if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f_even++ = y[0];
|
|
*f_even++ = v;
|
|
}
|
|
else {
|
|
y_ = 76284 * (y[0] - 16);
|
|
|
|
b_ = (y_ + vb) >> 16;
|
|
g_ = (y_ + uvg)>> 16;
|
|
r_ = (y_ + ur) >> 16;
|
|
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_RGB565:
|
|
g = LIMIT_RGB(g_);
|
|
*f_even++ =
|
|
(0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_even++ =
|
|
(0x07 & (g >> 3)) |
|
|
(0xF8 & LIMIT_RGB(b_));
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f_even++ = LIMIT_RGB(r_);
|
|
*f_even++ = LIMIT_RGB(g_);
|
|
*f_even++ = LIMIT_RGB(b_);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f_even++ = LIMIT_RGB(r_);
|
|
*f_even++ = LIMIT_RGB(g_);
|
|
*f_even++ = LIMIT_RGB(b_);
|
|
f_even++;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
g = LIMIT_RGB(g_);
|
|
*f_even++ = (0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_even++ = (0x03 & (g >> 3)) |
|
|
(0x7C & (LIMIT_RGB(b_) << 2));
|
|
break;
|
|
}
|
|
}
|
|
clipmask_even_index += clipmask_add;
|
|
f_even += stretch_bytes;
|
|
|
|
if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f_even++ = y[1];
|
|
*f_even++ = u;
|
|
}
|
|
else {
|
|
y_ = 76284 * (y[1] - 16);
|
|
|
|
b_ = (y_ + vb) >> 16;
|
|
g_ = (y_ + uvg)>> 16;
|
|
r_ = (y_ + ur) >> 16;
|
|
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_RGB565:
|
|
g = LIMIT_RGB(g_);
|
|
*f_even++ =
|
|
(0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_even++ =
|
|
(0x07 & (g >> 3)) |
|
|
(0xF8 & LIMIT_RGB(b_));
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f_even++ = LIMIT_RGB(r_);
|
|
*f_even++ = LIMIT_RGB(g_);
|
|
*f_even++ = LIMIT_RGB(b_);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f_even++ = LIMIT_RGB(r_);
|
|
*f_even++ = LIMIT_RGB(g_);
|
|
*f_even++ = LIMIT_RGB(b_);
|
|
f_even++;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
g = LIMIT_RGB(g_);
|
|
*f_even++ = (0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_even++ = (0x03 & (g >> 3)) |
|
|
(0x7C & (LIMIT_RGB(b_) << 2));
|
|
break;
|
|
}
|
|
}
|
|
clipmask_even_index += clipmask_add;
|
|
f_even += stretch_bytes;
|
|
|
|
scratch_get_extra(usbvision, &y[0], &y_ptr, 2);
|
|
|
|
if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f_odd++ = y[0];
|
|
*f_odd++ = v;
|
|
}
|
|
else {
|
|
y_ = 76284 * (y[0] - 16);
|
|
|
|
b_ = (y_ + vb) >> 16;
|
|
g_ = (y_ + uvg)>> 16;
|
|
r_ = (y_ + ur) >> 16;
|
|
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_RGB565:
|
|
g = LIMIT_RGB(g_);
|
|
*f_odd++ =
|
|
(0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_odd++ =
|
|
(0x07 & (g >> 3)) |
|
|
(0xF8 & LIMIT_RGB(b_));
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f_odd++ = LIMIT_RGB(r_);
|
|
*f_odd++ = LIMIT_RGB(g_);
|
|
*f_odd++ = LIMIT_RGB(b_);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f_odd++ = LIMIT_RGB(r_);
|
|
*f_odd++ = LIMIT_RGB(g_);
|
|
*f_odd++ = LIMIT_RGB(b_);
|
|
f_odd++;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
g = LIMIT_RGB(g_);
|
|
*f_odd++ = (0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_odd++ = (0x03 & (g >> 3)) |
|
|
(0x7C & (LIMIT_RGB(b_) << 2));
|
|
break;
|
|
}
|
|
}
|
|
clipmask_odd_index += clipmask_add;
|
|
f_odd += stretch_bytes;
|
|
|
|
if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
|
|
*f_odd++ = y[1];
|
|
*f_odd++ = u;
|
|
}
|
|
else {
|
|
y_ = 76284 * (y[1] - 16);
|
|
|
|
b_ = (y_ + vb) >> 16;
|
|
g_ = (y_ + uvg)>> 16;
|
|
r_ = (y_ + ur) >> 16;
|
|
|
|
switch (frame->v4l2_format.format) {
|
|
case V4L2_PIX_FMT_RGB565:
|
|
g = LIMIT_RGB(g_);
|
|
*f_odd++ =
|
|
(0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_odd++ =
|
|
(0x07 & (g >> 3)) |
|
|
(0xF8 & LIMIT_RGB(b_));
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
*f_odd++ = LIMIT_RGB(r_);
|
|
*f_odd++ = LIMIT_RGB(g_);
|
|
*f_odd++ = LIMIT_RGB(b_);
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
*f_odd++ = LIMIT_RGB(r_);
|
|
*f_odd++ = LIMIT_RGB(g_);
|
|
*f_odd++ = LIMIT_RGB(b_);
|
|
f_odd++;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB555:
|
|
g = LIMIT_RGB(g_);
|
|
*f_odd++ = (0x1F & LIMIT_RGB(r_)) |
|
|
(0xE0 & (g << 5));
|
|
*f_odd++ = (0x03 & (g >> 3)) |
|
|
(0x7C & (LIMIT_RGB(b_) << 2));
|
|
break;
|
|
}
|
|
}
|
|
clipmask_odd_index += clipmask_add;
|
|
f_odd += stretch_bytes;
|
|
}
|
|
|
|
scratch_rm_old(usbvision,y_step[block % y_step_size] * sub_block_size);
|
|
scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size]
|
|
* sub_block_size);
|
|
scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size]
|
|
* sub_block_size);
|
|
scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size]
|
|
* sub_block_size);
|
|
}
|
|
|
|
scratch_rm_old(usbvision, pixel_per_line * 3 / 2
|
|
+ block_split * sub_block_size);
|
|
|
|
frame->curline += 2 * usbvision->stretch_height;
|
|
*pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height;
|
|
|
|
if (frame->curline >= frame->frmheight)
|
|
return ParseState_NextFrame;
|
|
else
|
|
return ParseState_Continue;
|
|
}
|
|
|
|
/*
|
|
* usbvision_parse_data()
|
|
*
|
|
* Generic routine to parse the scratch buffer. It employs either
|
|
* usbvision_find_header() or usbvision_parse_lines() to do most
|
|
* of work.
|
|
*
|
|
*/
|
|
static void usbvision_parse_data(struct usb_usbvision *usbvision)
|
|
{
|
|
struct usbvision_frame *frame;
|
|
enum ParseState newstate;
|
|
long copylen = 0;
|
|
unsigned long lock_flags;
|
|
|
|
frame = usbvision->curFrame;
|
|
|
|
PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision));
|
|
|
|
while (1) {
|
|
|
|
newstate = ParseState_Out;
|
|
if (scratch_len(usbvision)) {
|
|
if (frame->scanstate == ScanState_Scanning) {
|
|
newstate = usbvision_find_header(usbvision);
|
|
}
|
|
else if (frame->scanstate == ScanState_Lines) {
|
|
if (usbvision->isocMode == ISOC_MODE_YUV420) {
|
|
newstate = usbvision_parse_lines_420(usbvision, ©len);
|
|
}
|
|
else if (usbvision->isocMode == ISOC_MODE_YUV422) {
|
|
newstate = usbvision_parse_lines_422(usbvision, ©len);
|
|
}
|
|
else if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
|
|
newstate = usbvision_parse_compress(usbvision, ©len);
|
|
}
|
|
|
|
}
|
|
}
|
|
if (newstate == ParseState_Continue) {
|
|
continue;
|
|
}
|
|
else if ((newstate == ParseState_NextFrame) || (newstate == ParseState_Out)) {
|
|
break;
|
|
}
|
|
else {
|
|
return; /* ParseState_EndParse */
|
|
}
|
|
}
|
|
|
|
if (newstate == ParseState_NextFrame) {
|
|
frame->grabstate = FrameState_Done;
|
|
do_gettimeofday(&(frame->timestamp));
|
|
frame->sequence = usbvision->frame_num;
|
|
|
|
spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
|
|
list_move_tail(&(frame->frame), &usbvision->outqueue);
|
|
usbvision->curFrame = NULL;
|
|
spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
|
|
|
|
usbvision->frame_num++;
|
|
|
|
/* This will cause the process to request another frame. */
|
|
if (waitqueue_active(&usbvision->wait_frame)) {
|
|
PDEBUG(DBG_PARSE, "Wake up !");
|
|
wake_up_interruptible(&usbvision->wait_frame);
|
|
}
|
|
}
|
|
else
|
|
frame->grabstate = FrameState_Grabbing;
|
|
|
|
|
|
/* Update the frame's uncompressed length. */
|
|
frame->scanlength += copylen;
|
|
}
|
|
|
|
|
|
/*
|
|
* Make all of the blocks of data contiguous
|
|
*/
|
|
static int usbvision_compress_isochronous(struct usb_usbvision *usbvision,
|
|
struct urb *urb)
|
|
{
|
|
unsigned char *packet_data;
|
|
int i, totlen = 0;
|
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
int packet_len = urb->iso_frame_desc[i].actual_length;
|
|
int packet_stat = urb->iso_frame_desc[i].status;
|
|
|
|
packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
|
|
|
|
/* Detect and ignore errored packets */
|
|
if (packet_stat) { // packet_stat != 0 ?????????????
|
|
PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat);
|
|
usbvision->isocErrCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Detect and ignore empty packets */
|
|
if (packet_len < 0) {
|
|
PDEBUG(DBG_ISOC, "error packet [%d]", i);
|
|
usbvision->isocSkipCount++;
|
|
continue;
|
|
}
|
|
else if (packet_len == 0) { /* Frame end ????? */
|
|
PDEBUG(DBG_ISOC, "null packet [%d]", i);
|
|
usbvision->isocstate=IsocState_NoFrame;
|
|
usbvision->isocSkipCount++;
|
|
continue;
|
|
}
|
|
else if (packet_len > usbvision->isocPacketSize) {
|
|
PDEBUG(DBG_ISOC, "packet[%d] > isocPacketSize", i);
|
|
usbvision->isocSkipCount++;
|
|
continue;
|
|
}
|
|
|
|
PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len);
|
|
|
|
if (usbvision->isocstate==IsocState_NoFrame) { //new frame begins
|
|
usbvision->isocstate=IsocState_InFrame;
|
|
scratch_mark_header(usbvision);
|
|
usbvision_measure_bandwidth(usbvision);
|
|
PDEBUG(DBG_ISOC, "packet with header");
|
|
}
|
|
|
|
/*
|
|
* If usbvision continues to feed us with data but there is no
|
|
* consumption (if, for example, V4L client fell asleep) we
|
|
* may overflow the buffer. We have to move old data over to
|
|
* free room for new data. This is bad for old data. If we
|
|
* just drop new data then it's bad for new data... choose
|
|
* your favorite evil here.
|
|
*/
|
|
if (scratch_free(usbvision) < packet_len) {
|
|
|
|
usbvision->scratch_ovf_count++;
|
|
PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d",
|
|
scratch_len(usbvision), packet_len);
|
|
scratch_rm_old(usbvision, packet_len - scratch_free(usbvision));
|
|
}
|
|
|
|
/* Now we know that there is enough room in scratch buffer */
|
|
scratch_put(usbvision, packet_data, packet_len);
|
|
totlen += packet_len;
|
|
usbvision->isocDataCount += packet_len;
|
|
usbvision->isocPacketCount++;
|
|
}
|
|
#if ENABLE_HEXDUMP
|
|
if (totlen > 0) {
|
|
static int foo;
|
|
if (foo < 1) {
|
|
printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen);
|
|
usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen);
|
|
++foo;
|
|
}
|
|
}
|
|
#endif
|
|
return totlen;
|
|
}
|
|
|
|
static void usbvision_isocIrq(struct urb *urb)
|
|
{
|
|
int errCode = 0;
|
|
int len;
|
|
struct usb_usbvision *usbvision = urb->context;
|
|
int i;
|
|
unsigned long startTime = jiffies;
|
|
struct usbvision_frame **f;
|
|
|
|
/* We don't want to do anything if we are about to be removed! */
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return;
|
|
|
|
/* any urb with wrong status is ignored without acknowledgement */
|
|
if (urb->status == -ENOENT) {
|
|
return;
|
|
}
|
|
|
|
f = &usbvision->curFrame;
|
|
|
|
/* Manage streaming interruption */
|
|
if (usbvision->streaming == Stream_Interrupt) {
|
|
usbvision->streaming = Stream_Idle;
|
|
if ((*f)) {
|
|
(*f)->grabstate = FrameState_Ready;
|
|
(*f)->scanstate = ScanState_Scanning;
|
|
}
|
|
PDEBUG(DBG_IRQ, "stream interrupted");
|
|
wake_up_interruptible(&usbvision->wait_stream);
|
|
}
|
|
|
|
/* Copy the data received into our scratch buffer */
|
|
len = usbvision_compress_isochronous(usbvision, urb);
|
|
|
|
usbvision->isocUrbCount++;
|
|
usbvision->urb_length = len;
|
|
|
|
if (usbvision->streaming == Stream_On) {
|
|
|
|
/* If we collected enough data let's parse! */
|
|
if ((scratch_len(usbvision) > USBVISION_HEADER_LENGTH) &&
|
|
(!list_empty(&(usbvision->inqueue))) ) {
|
|
if (!(*f)) {
|
|
(*f) = list_entry(usbvision->inqueue.next,
|
|
struct usbvision_frame,
|
|
frame);
|
|
}
|
|
usbvision_parse_data(usbvision);
|
|
}
|
|
else {
|
|
/*If we don't have a frame
|
|
we're current working on, complain */
|
|
PDEBUG(DBG_IRQ,
|
|
"received data, but no one needs it");
|
|
scratch_reset(usbvision);
|
|
}
|
|
}
|
|
else {
|
|
PDEBUG(DBG_IRQ, "received data, but no one needs it");
|
|
scratch_reset(usbvision);
|
|
}
|
|
|
|
usbvision->timeInIrq += jiffies - startTime;
|
|
|
|
for (i = 0; i < USBVISION_URB_FRAMES; i++) {
|
|
urb->iso_frame_desc[i].status = 0;
|
|
urb->iso_frame_desc[i].actual_length = 0;
|
|
}
|
|
|
|
urb->status = 0;
|
|
urb->dev = usbvision->dev;
|
|
errCode = usb_submit_urb (urb, GFP_ATOMIC);
|
|
|
|
if(errCode) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: usb_submit_urb failed: error %d\n",
|
|
__func__, errCode);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************/
|
|
/* Low level usbvision access functions */
|
|
/*************************************/
|
|
|
|
/*
|
|
* usbvision_read_reg()
|
|
*
|
|
* return < 0 -> Error
|
|
* >= 0 -> Data
|
|
*/
|
|
|
|
int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg)
|
|
{
|
|
int errCode = 0;
|
|
unsigned char buffer[1];
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return -1;
|
|
|
|
errCode = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE,
|
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
|
|
0, (__u16) reg, buffer, 1, HZ);
|
|
|
|
if (errCode < 0) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: failed: error %d\n", __func__, errCode);
|
|
return errCode;
|
|
}
|
|
return buffer[0];
|
|
}
|
|
|
|
/*
|
|
* usbvision_write_reg()
|
|
*
|
|
* return 1 -> Reg written
|
|
* 0 -> usbvision is not yet ready
|
|
* -1 -> Something went wrong
|
|
*/
|
|
|
|
int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
|
|
unsigned char value)
|
|
{
|
|
int errCode = 0;
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return 0;
|
|
|
|
errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR |
|
|
USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ);
|
|
|
|
if (errCode < 0) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: failed: error %d\n", __func__, errCode);
|
|
}
|
|
return errCode;
|
|
}
|
|
|
|
|
|
static void usbvision_ctrlUrb_complete(struct urb *urb)
|
|
{
|
|
struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context;
|
|
|
|
PDEBUG(DBG_IRQ, "");
|
|
usbvision->ctrlUrbBusy = 0;
|
|
if (waitqueue_active(&usbvision->ctrlUrb_wq)) {
|
|
wake_up_interruptible(&usbvision->ctrlUrb_wq);
|
|
}
|
|
}
|
|
|
|
|
|
static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address,
|
|
unsigned char *data, int len)
|
|
{
|
|
int errCode = 0;
|
|
|
|
PDEBUG(DBG_IRQ, "");
|
|
if (len > 8) {
|
|
return -EFAULT;
|
|
}
|
|
if (usbvision->ctrlUrbBusy) {
|
|
return -EBUSY;
|
|
}
|
|
usbvision->ctrlUrbBusy = 1;
|
|
|
|
usbvision->ctrlUrbSetup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
|
|
usbvision->ctrlUrbSetup.bRequest = USBVISION_OP_CODE;
|
|
usbvision->ctrlUrbSetup.wValue = 0;
|
|
usbvision->ctrlUrbSetup.wIndex = cpu_to_le16(address);
|
|
usbvision->ctrlUrbSetup.wLength = cpu_to_le16(len);
|
|
usb_fill_control_urb (usbvision->ctrlUrb, usbvision->dev,
|
|
usb_sndctrlpipe(usbvision->dev, 1),
|
|
(unsigned char *)&usbvision->ctrlUrbSetup,
|
|
(void *)usbvision->ctrlUrbBuffer, len,
|
|
usbvision_ctrlUrb_complete,
|
|
(void *)usbvision);
|
|
|
|
memcpy(usbvision->ctrlUrbBuffer, data, len);
|
|
|
|
errCode = usb_submit_urb(usbvision->ctrlUrb, GFP_ATOMIC);
|
|
if (errCode < 0) {
|
|
// error in usb_submit_urb()
|
|
usbvision->ctrlUrbBusy = 0;
|
|
}
|
|
PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, errCode);
|
|
return errCode;
|
|
}
|
|
|
|
|
|
static int usbvision_init_compression(struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
|
|
usbvision->lastIsocFrameNum = -1;
|
|
usbvision->isocDataCount = 0;
|
|
usbvision->isocPacketCount = 0;
|
|
usbvision->isocSkipCount = 0;
|
|
usbvision->comprLevel = 50;
|
|
usbvision->lastComprLevel = -1;
|
|
usbvision->isocUrbCount = 0;
|
|
usbvision->requestIntra = 1;
|
|
usbvision->isocMeasureBandwidthCount = 0;
|
|
|
|
return errCode;
|
|
}
|
|
|
|
/* this function measures the used bandwidth since last call
|
|
* return: 0 : no error
|
|
* sets usedBandwidth to 1-100 : 1-100% of full bandwidth resp. to isocPacketSize
|
|
*/
|
|
static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
|
|
if (usbvision->isocMeasureBandwidthCount < 2) { // this gives an average bandwidth of 3 frames
|
|
usbvision->isocMeasureBandwidthCount++;
|
|
return errCode;
|
|
}
|
|
if ((usbvision->isocPacketSize > 0) && (usbvision->isocPacketCount > 0)) {
|
|
usbvision->usedBandwidth = usbvision->isocDataCount /
|
|
(usbvision->isocPacketCount + usbvision->isocSkipCount) *
|
|
100 / usbvision->isocPacketSize;
|
|
}
|
|
usbvision->isocMeasureBandwidthCount = 0;
|
|
usbvision->isocDataCount = 0;
|
|
usbvision->isocPacketCount = 0;
|
|
usbvision->isocSkipCount = 0;
|
|
return errCode;
|
|
}
|
|
|
|
static int usbvision_adjust_compression (struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
unsigned char buffer[6];
|
|
|
|
PDEBUG(DBG_IRQ, "");
|
|
if ((adjustCompression) && (usbvision->usedBandwidth > 0)) {
|
|
usbvision->comprLevel += (usbvision->usedBandwidth - 90) / 2;
|
|
RESTRICT_TO_RANGE(usbvision->comprLevel, 0, 100);
|
|
if (usbvision->comprLevel != usbvision->lastComprLevel) {
|
|
int distorsion;
|
|
if (usbvision->bridgeType == BRIDGE_NT1004 || usbvision->bridgeType == BRIDGE_NT1005) {
|
|
buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100); // PCM Threshold 1
|
|
buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100); // PCM Threshold 2
|
|
distorsion = 7 + 248 * usbvision->comprLevel / 100;
|
|
buffer[2] = (unsigned char)(distorsion & 0xFF); // Average distorsion Threshold (inter)
|
|
buffer[3] = (unsigned char)(distorsion & 0xFF); // Average distorsion Threshold (intra)
|
|
distorsion = 1 + 42 * usbvision->comprLevel / 100;
|
|
buffer[4] = (unsigned char)(distorsion & 0xFF); // Maximum distorsion Threshold (inter)
|
|
buffer[5] = (unsigned char)(distorsion & 0xFF); // Maximum distorsion Threshold (intra)
|
|
}
|
|
else { //BRIDGE_NT1003
|
|
buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100); // PCM threshold 1
|
|
buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100); // PCM threshold 2
|
|
distorsion = 2 + 253 * usbvision->comprLevel / 100;
|
|
buffer[2] = (unsigned char)(distorsion & 0xFF); // distorsion threshold bit0-7
|
|
buffer[3] = 0; //(unsigned char)((distorsion >> 8) & 0x0F); // distorsion threshold bit 8-11
|
|
distorsion = 0 + 43 * usbvision->comprLevel / 100;
|
|
buffer[4] = (unsigned char)(distorsion & 0xFF); // maximum distorsion bit0-7
|
|
buffer[5] = 0; //(unsigned char)((distorsion >> 8) & 0x01); // maximum distorsion bit 8
|
|
}
|
|
errCode = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6);
|
|
if (errCode == 0){
|
|
PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0],
|
|
buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
|
|
usbvision->lastComprLevel = usbvision->comprLevel;
|
|
}
|
|
}
|
|
}
|
|
return errCode;
|
|
}
|
|
|
|
static int usbvision_request_intra (struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
unsigned char buffer[1];
|
|
|
|
PDEBUG(DBG_IRQ, "");
|
|
usbvision->requestIntra = 1;
|
|
buffer[0] = 1;
|
|
usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
|
|
return errCode;
|
|
}
|
|
|
|
static int usbvision_unrequest_intra (struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
unsigned char buffer[1];
|
|
|
|
PDEBUG(DBG_IRQ, "");
|
|
usbvision->requestIntra = 0;
|
|
buffer[0] = 0;
|
|
usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
|
|
return errCode;
|
|
}
|
|
|
|
/*******************************
|
|
* usbvision utility functions
|
|
*******************************/
|
|
|
|
int usbvision_power_off(struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
|
|
PDEBUG(DBG_FUNC, "");
|
|
|
|
errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
|
|
if (errCode == 1) {
|
|
usbvision->power = 0;
|
|
}
|
|
PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode!=1)?"ERROR":"power is off", errCode);
|
|
return errCode;
|
|
}
|
|
|
|
/*
|
|
* usbvision_set_video_format()
|
|
*
|
|
*/
|
|
static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format)
|
|
{
|
|
static const char proc[] = "usbvision_set_video_format";
|
|
int rc;
|
|
unsigned char value[2];
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return 0;
|
|
|
|
PDEBUG(DBG_FUNC, "isocMode %#02x", format);
|
|
|
|
if ((format != ISOC_MODE_YUV422)
|
|
&& (format != ISOC_MODE_YUV420)
|
|
&& (format != ISOC_MODE_COMPRESS)) {
|
|
printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420",
|
|
format);
|
|
format = ISOC_MODE_YUV420;
|
|
}
|
|
value[0] = 0x0A; //TODO: See the effect of the filter
|
|
value[1] = format; // Sets the VO_MODE register which follows FILT_CONT
|
|
rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR |
|
|
USB_RECIP_ENDPOINT, 0,
|
|
(__u16) USBVISION_FILT_CONT, value, 2, HZ);
|
|
|
|
if (rc < 0) {
|
|
printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - "
|
|
"reconnect or reload driver.\n", proc, rc);
|
|
}
|
|
usbvision->isocMode = format;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* usbvision_set_output()
|
|
*
|
|
*/
|
|
|
|
int usbvision_set_output(struct usb_usbvision *usbvision, int width,
|
|
int height)
|
|
{
|
|
int errCode = 0;
|
|
int UsbWidth, UsbHeight;
|
|
unsigned int frameRate=0, frameDrop=0;
|
|
unsigned char value[4];
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision)) {
|
|
return 0;
|
|
}
|
|
|
|
if (width > MAX_USB_WIDTH) {
|
|
UsbWidth = width / 2;
|
|
usbvision->stretch_width = 2;
|
|
}
|
|
else {
|
|
UsbWidth = width;
|
|
usbvision->stretch_width = 1;
|
|
}
|
|
|
|
if (height > MAX_USB_HEIGHT) {
|
|
UsbHeight = height / 2;
|
|
usbvision->stretch_height = 2;
|
|
}
|
|
else {
|
|
UsbHeight = height;
|
|
usbvision->stretch_height = 1;
|
|
}
|
|
|
|
RESTRICT_TO_RANGE(UsbWidth, MIN_FRAME_WIDTH, MAX_USB_WIDTH);
|
|
UsbWidth &= ~(MIN_FRAME_WIDTH-1);
|
|
RESTRICT_TO_RANGE(UsbHeight, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT);
|
|
UsbHeight &= ~(1);
|
|
|
|
PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d",
|
|
UsbWidth, UsbHeight, width, height,
|
|
usbvision->stretch_width, usbvision->stretch_height);
|
|
|
|
/* I'll not rewrite the same values */
|
|
if ((UsbWidth != usbvision->curwidth) || (UsbHeight != usbvision->curheight)) {
|
|
value[0] = UsbWidth & 0xff; //LSB
|
|
value[1] = (UsbWidth >> 8) & 0x03; //MSB
|
|
value[2] = UsbHeight & 0xff; //LSB
|
|
value[3] = (UsbHeight >> 8) & 0x03; //MSB
|
|
|
|
errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
|
|
0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ);
|
|
|
|
if (errCode < 0) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s failed: error %d\n", __func__, errCode);
|
|
return errCode;
|
|
}
|
|
usbvision->curwidth = usbvision->stretch_width * UsbWidth;
|
|
usbvision->curheight = usbvision->stretch_height * UsbHeight;
|
|
}
|
|
|
|
if (usbvision->isocMode == ISOC_MODE_YUV422) {
|
|
frameRate = (usbvision->isocPacketSize * 1000) / (UsbWidth * UsbHeight * 2);
|
|
}
|
|
else if (usbvision->isocMode == ISOC_MODE_YUV420) {
|
|
frameRate = (usbvision->isocPacketSize * 1000) / ((UsbWidth * UsbHeight * 12) / 8);
|
|
}
|
|
else {
|
|
frameRate = FRAMERATE_MAX;
|
|
}
|
|
|
|
if (usbvision->tvnormId & V4L2_STD_625_50) {
|
|
frameDrop = frameRate * 32 / 25 - 1;
|
|
}
|
|
else if (usbvision->tvnormId & V4L2_STD_525_60) {
|
|
frameDrop = frameRate * 32 / 30 - 1;
|
|
}
|
|
|
|
RESTRICT_TO_RANGE(frameDrop, FRAMERATE_MIN, FRAMERATE_MAX);
|
|
|
|
PDEBUG(DBG_FUNC, "frameRate %d fps, frameDrop %d", frameRate, frameDrop);
|
|
|
|
frameDrop = FRAMERATE_MAX; // We can allow the maximum here, because dropping is controlled
|
|
|
|
/* frameDrop = 7; => framePhase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ...
|
|
=> frameSkip = 4;
|
|
=> frameRate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25;
|
|
|
|
frameDrop = 9; => framePhase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ...
|
|
=> frameSkip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ...
|
|
=> frameRate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125;
|
|
*/
|
|
errCode = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frameDrop);
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/*
|
|
* usbvision_frames_alloc
|
|
* allocate the required frames
|
|
*/
|
|
int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames)
|
|
{
|
|
int i;
|
|
|
|
/*needs to be page aligned cause the buffers can be mapped individually! */
|
|
usbvision->max_frame_size = PAGE_ALIGN(usbvision->curwidth *
|
|
usbvision->curheight *
|
|
usbvision->palette.bytes_per_pixel);
|
|
|
|
/* Try to do my best to allocate the frames the user want in the remaining memory */
|
|
usbvision->num_frames = number_of_frames;
|
|
while (usbvision->num_frames > 0) {
|
|
usbvision->fbuf_size = usbvision->num_frames * usbvision->max_frame_size;
|
|
if((usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size))) {
|
|
break;
|
|
}
|
|
usbvision->num_frames--;
|
|
}
|
|
|
|
spin_lock_init(&usbvision->queue_lock);
|
|
init_waitqueue_head(&usbvision->wait_frame);
|
|
init_waitqueue_head(&usbvision->wait_stream);
|
|
|
|
/* Allocate all buffers */
|
|
for (i = 0; i < usbvision->num_frames; i++) {
|
|
usbvision->frame[i].index = i;
|
|
usbvision->frame[i].grabstate = FrameState_Unused;
|
|
usbvision->frame[i].data = usbvision->fbuf +
|
|
i * usbvision->max_frame_size;
|
|
/*
|
|
* Set default sizes for read operation.
|
|
*/
|
|
usbvision->stretch_width = 1;
|
|
usbvision->stretch_height = 1;
|
|
usbvision->frame[i].width = usbvision->curwidth;
|
|
usbvision->frame[i].height = usbvision->curheight;
|
|
usbvision->frame[i].bytes_read = 0;
|
|
}
|
|
PDEBUG(DBG_FUNC, "allocated %d frames (%d bytes per frame)",usbvision->num_frames,usbvision->max_frame_size);
|
|
return usbvision->num_frames;
|
|
}
|
|
|
|
/*
|
|
* usbvision_frames_free
|
|
* frees memory allocated for the frames
|
|
*/
|
|
void usbvision_frames_free(struct usb_usbvision *usbvision)
|
|
{
|
|
/* Have to free all that memory */
|
|
PDEBUG(DBG_FUNC, "free %d frames",usbvision->num_frames);
|
|
|
|
if (usbvision->fbuf != NULL) {
|
|
usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
|
|
usbvision->fbuf = NULL;
|
|
|
|
usbvision->num_frames = 0;
|
|
}
|
|
}
|
|
/*
|
|
* usbvision_empty_framequeues()
|
|
* prepare queues for incoming and outgoing frames
|
|
*/
|
|
void usbvision_empty_framequeues(struct usb_usbvision *usbvision)
|
|
{
|
|
u32 i;
|
|
|
|
INIT_LIST_HEAD(&(usbvision->inqueue));
|
|
INIT_LIST_HEAD(&(usbvision->outqueue));
|
|
|
|
for (i = 0; i < USBVISION_NUMFRAMES; i++) {
|
|
usbvision->frame[i].grabstate = FrameState_Unused;
|
|
usbvision->frame[i].bytes_read = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* usbvision_stream_interrupt()
|
|
* stops streaming
|
|
*/
|
|
int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* stop reading from the device */
|
|
|
|
usbvision->streaming = Stream_Interrupt;
|
|
ret = wait_event_timeout(usbvision->wait_stream,
|
|
(usbvision->streaming == Stream_Idle),
|
|
msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES));
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* usbvision_set_compress_params()
|
|
*
|
|
*/
|
|
|
|
static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
|
|
{
|
|
static const char proc[] = "usbvision_set_compresion_params: ";
|
|
int rc;
|
|
unsigned char value[6];
|
|
|
|
value[0] = 0x0F; // Intra-Compression cycle
|
|
value[1] = 0x01; // Reg.45 one line per strip
|
|
value[2] = 0x00; // Reg.46 Force intra mode on all new frames
|
|
value[3] = 0x00; // Reg.47 FORCE_UP <- 0 normal operation (not force)
|
|
value[4] = 0xA2; // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode.
|
|
value[5] = 0x00; // Reg.49 DVI_YUV This has nothing to do with compression
|
|
|
|
//catched values for NT1004
|
|
// value[0] = 0xFF; // Never apply intra mode automatically
|
|
// value[1] = 0xF1; // Use full frame height for virtual strip width; One line per strip
|
|
// value[2] = 0x01; // Force intra mode on all new frames
|
|
// value[3] = 0x00; // Strip size 400 Bytes; do not force up
|
|
// value[4] = 0xA2; //
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return 0;
|
|
|
|
rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR |
|
|
USB_RECIP_ENDPOINT, 0,
|
|
(__u16) USBVISION_INTRA_CYC, value, 5, HZ);
|
|
|
|
if (rc < 0) {
|
|
printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
|
|
"reconnect or reload driver.\n", proc, rc);
|
|
return rc;
|
|
}
|
|
|
|
if (usbvision->bridgeType == BRIDGE_NT1004) {
|
|
value[0] = 20; // PCM Threshold 1
|
|
value[1] = 12; // PCM Threshold 2
|
|
value[2] = 255; // Distorsion Threshold inter
|
|
value[3] = 255; // Distorsion Threshold intra
|
|
value[4] = 43; // Max Distorsion inter
|
|
value[5] = 43; // Max Distorsion intra
|
|
}
|
|
else {
|
|
value[0] = 20; // PCM Threshold 1
|
|
value[1] = 12; // PCM Threshold 2
|
|
value[2] = 255; // Distorsion Threshold d7-d0
|
|
value[3] = 0; // Distorsion Threshold d11-d8
|
|
value[4] = 43; // Max Distorsion d7-d0
|
|
value[5] = 0; // Max Distorsion d8
|
|
}
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return 0;
|
|
|
|
rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE,
|
|
USB_DIR_OUT | USB_TYPE_VENDOR |
|
|
USB_RECIP_ENDPOINT, 0,
|
|
(__u16) USBVISION_PCM_THR1, value, 6, HZ);
|
|
|
|
if (rc < 0) {
|
|
printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
|
|
"reconnect or reload driver.\n", proc, rc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* usbvision_set_input()
|
|
*
|
|
* Set the input (saa711x, ...) size x y and other misc input params
|
|
* I've no idea if this parameters are right
|
|
*
|
|
*/
|
|
int usbvision_set_input(struct usb_usbvision *usbvision)
|
|
{
|
|
static const char proc[] = "usbvision_set_input: ";
|
|
int rc;
|
|
unsigned char value[8];
|
|
unsigned char dvi_yuv_value;
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return 0;
|
|
|
|
/* Set input format expected from decoder*/
|
|
if (usbvision_device_data[usbvision->DevModel].Vin_Reg1_override) {
|
|
value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1;
|
|
} else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
|
|
/* SAA7113 uses 8 bit output */
|
|
value[0] = USBVISION_8_422_SYNC;
|
|
} else {
|
|
/* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
|
|
* as that is how saa7111 is configured */
|
|
value[0] = USBVISION_16_422_SYNC;
|
|
/* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
|
|
}
|
|
|
|
rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]);
|
|
if (rc < 0) {
|
|
printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
|
|
"reconnect or reload driver.\n", proc, rc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
if (usbvision->tvnormId & V4L2_STD_PAL) {
|
|
value[0] = 0xC0;
|
|
value[1] = 0x02; //0x02C0 -> 704 Input video line length
|
|
value[2] = 0x20;
|
|
value[3] = 0x01; //0x0120 -> 288 Input video n. of lines
|
|
value[4] = 0x60;
|
|
value[5] = 0x00; //0x0060 -> 96 Input video h offset
|
|
value[6] = 0x16;
|
|
value[7] = 0x00; //0x0016 -> 22 Input video v offset
|
|
} else if (usbvision->tvnormId & V4L2_STD_SECAM) {
|
|
value[0] = 0xC0;
|
|
value[1] = 0x02; //0x02C0 -> 704 Input video line length
|
|
value[2] = 0x20;
|
|
value[3] = 0x01; //0x0120 -> 288 Input video n. of lines
|
|
value[4] = 0x01;
|
|
value[5] = 0x00; //0x0001 -> 01 Input video h offset
|
|
value[6] = 0x01;
|
|
value[7] = 0x00; //0x0001 -> 01 Input video v offset
|
|
} else { /* V4L2_STD_NTSC */
|
|
value[0] = 0xD0;
|
|
value[1] = 0x02; //0x02D0 -> 720 Input video line length
|
|
value[2] = 0xF0;
|
|
value[3] = 0x00; //0x00F0 -> 240 Input video number of lines
|
|
value[4] = 0x50;
|
|
value[5] = 0x00; //0x0050 -> 80 Input video h offset
|
|
value[6] = 0x10;
|
|
value[7] = 0x00; //0x0010 -> 16 Input video v offset
|
|
}
|
|
|
|
if (usbvision_device_data[usbvision->DevModel].X_Offset >= 0) {
|
|
value[4]=usbvision_device_data[usbvision->DevModel].X_Offset & 0xff;
|
|
value[5]=(usbvision_device_data[usbvision->DevModel].X_Offset & 0x0300) >> 8;
|
|
}
|
|
|
|
if (adjust_X_Offset != -1) {
|
|
value[4] = adjust_X_Offset & 0xff;
|
|
value[5] = (adjust_X_Offset & 0x0300) >> 8;
|
|
}
|
|
|
|
if (usbvision_device_data[usbvision->DevModel].Y_Offset >= 0) {
|
|
value[6]=usbvision_device_data[usbvision->DevModel].Y_Offset & 0xff;
|
|
value[7]=(usbvision_device_data[usbvision->DevModel].Y_Offset & 0x0300) >> 8;
|
|
}
|
|
|
|
if (adjust_Y_Offset != -1) {
|
|
value[6] = adjust_Y_Offset & 0xff;
|
|
value[7] = (adjust_Y_Offset & 0x0300) >> 8;
|
|
}
|
|
|
|
rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE, /* USBVISION specific code */
|
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0,
|
|
(__u16) USBVISION_LXSIZE_I, value, 8, HZ);
|
|
if (rc < 0) {
|
|
printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
|
|
"reconnect or reload driver.\n", proc, rc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */
|
|
|
|
if(usbvision_device_data[usbvision->DevModel].Dvi_yuv_override){
|
|
dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv;
|
|
}
|
|
else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
|
|
/* This changes as the fine sync control changes. Further investigation necessary */
|
|
dvi_yuv_value = 0x06;
|
|
}
|
|
|
|
return (usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value));
|
|
}
|
|
|
|
|
|
/*
|
|
* usbvision_set_dram_settings()
|
|
*
|
|
* Set the buffer address needed by the usbvision dram to operate
|
|
* This values has been taken with usbsnoop.
|
|
*
|
|
*/
|
|
|
|
static int usbvision_set_dram_settings(struct usb_usbvision *usbvision)
|
|
{
|
|
int rc;
|
|
unsigned char value[8];
|
|
|
|
if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
|
|
value[0] = 0x42;
|
|
value[1] = 0x71;
|
|
value[2] = 0xff;
|
|
value[3] = 0x00;
|
|
value[4] = 0x98;
|
|
value[5] = 0xe0;
|
|
value[6] = 0x71;
|
|
value[7] = 0xff;
|
|
// UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte)
|
|
// FDL: 0x00000-0x0E099 = 57498 Words
|
|
// VDW: 0x0E3FF-0x3FFFF
|
|
}
|
|
else {
|
|
value[0] = 0x42;
|
|
value[1] = 0x00;
|
|
value[2] = 0xff;
|
|
value[3] = 0x00;
|
|
value[4] = 0x00;
|
|
value[5] = 0x00;
|
|
value[6] = 0x00;
|
|
value[7] = 0xff;
|
|
}
|
|
/* These are the values of the address of the video buffer,
|
|
* they have to be loaded into the USBVISION_DRM_PRM1-8
|
|
*
|
|
* Start address of video output buffer for read: drm_prm1-2 -> 0x00000
|
|
* End address of video output buffer for read: drm_prm1-3 -> 0x1ffff
|
|
* Start address of video frame delay buffer: drm_prm1-4 -> 0x20000
|
|
* Only used in compressed mode
|
|
* End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff
|
|
* Only used in compressed mode
|
|
* Start address of video output buffer for write: drm_prm1-7 -> 0x00000
|
|
* End address of video output buffer for write: drm_prm1-8 -> 0x1ffff
|
|
*/
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return 0;
|
|
|
|
rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
|
|
USBVISION_OP_CODE, /* USBVISION specific code */
|
|
USB_DIR_OUT | USB_TYPE_VENDOR |
|
|
USB_RECIP_ENDPOINT, 0,
|
|
(__u16) USBVISION_DRM_PRM1, value, 8, HZ);
|
|
|
|
if (rc < 0) {
|
|
dev_err(&usbvision->dev->dev, "%sERROR=%d\n", __func__, rc);
|
|
return rc;
|
|
}
|
|
|
|
/* Restart the video buffer logic */
|
|
if ((rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR |
|
|
USBVISION_RES_FDL | USBVISION_RES_VDW)) < 0)
|
|
return rc;
|
|
rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* ()
|
|
*
|
|
* Power on the device, enables suspend-resume logic
|
|
* & reset the isoc End-Point
|
|
*
|
|
*/
|
|
|
|
int usbvision_power_on(struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
|
|
PDEBUG(DBG_FUNC, "");
|
|
|
|
usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
|
|
usbvision_write_reg(usbvision, USBVISION_PWR_REG,
|
|
USBVISION_SSPND_EN | USBVISION_RES2);
|
|
|
|
usbvision_write_reg(usbvision, USBVISION_PWR_REG,
|
|
USBVISION_SSPND_EN | USBVISION_PWR_VID);
|
|
errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
|
|
USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2);
|
|
if (errCode == 1) {
|
|
usbvision->power = 1;
|
|
}
|
|
PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode<0)?"ERROR":"power is on", errCode);
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/*
|
|
* usbvision timer stuff
|
|
*/
|
|
|
|
// to call usbvision_power_off from task queue
|
|
static void call_usbvision_power_off(struct work_struct *work)
|
|
{
|
|
struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, powerOffWork);
|
|
|
|
PDEBUG(DBG_FUNC, "");
|
|
if(mutex_lock_interruptible(&usbvision->lock)) {
|
|
return;
|
|
}
|
|
|
|
|
|
if(usbvision->user == 0) {
|
|
usbvision_i2c_unregister(usbvision);
|
|
|
|
usbvision_power_off(usbvision);
|
|
usbvision->initialized = 0;
|
|
}
|
|
mutex_unlock(&usbvision->lock);
|
|
}
|
|
|
|
static void usbvision_powerOffTimer(unsigned long data)
|
|
{
|
|
struct usb_usbvision *usbvision = (void *) data;
|
|
|
|
PDEBUG(DBG_FUNC, "");
|
|
del_timer(&usbvision->powerOffTimer);
|
|
INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off);
|
|
(void) schedule_work(&usbvision->powerOffWork);
|
|
}
|
|
|
|
void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision)
|
|
{
|
|
init_timer(&usbvision->powerOffTimer);
|
|
usbvision->powerOffTimer.data = (long) usbvision;
|
|
usbvision->powerOffTimer.function = usbvision_powerOffTimer;
|
|
}
|
|
|
|
void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision)
|
|
{
|
|
mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME);
|
|
}
|
|
|
|
void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision)
|
|
{
|
|
if (timer_pending(&usbvision->powerOffTimer)) {
|
|
del_timer(&usbvision->powerOffTimer);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* usbvision_begin_streaming()
|
|
* Sure you have to put bit 7 to 0, if not incoming frames are droped, but no
|
|
* idea about the rest
|
|
*/
|
|
int usbvision_begin_streaming(struct usb_usbvision *usbvision)
|
|
{
|
|
int errCode = 0;
|
|
|
|
if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
|
|
usbvision_init_compression(usbvision);
|
|
}
|
|
errCode = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, USBVISION_NOHVALID |
|
|
usbvision->Vin_Reg2_Preset);
|
|
return errCode;
|
|
}
|
|
|
|
/*
|
|
* usbvision_restart_isoc()
|
|
* Not sure yet if touching here PWR_REG make loose the config
|
|
*/
|
|
|
|
int usbvision_restart_isoc(struct usb_usbvision *usbvision)
|
|
{
|
|
int ret;
|
|
|
|
if (
|
|
(ret =
|
|
usbvision_write_reg(usbvision, USBVISION_PWR_REG,
|
|
USBVISION_SSPND_EN | USBVISION_PWR_VID)) < 0)
|
|
return ret;
|
|
if (
|
|
(ret =
|
|
usbvision_write_reg(usbvision, USBVISION_PWR_REG,
|
|
USBVISION_SSPND_EN | USBVISION_PWR_VID |
|
|
USBVISION_RES2)) < 0)
|
|
return ret;
|
|
if (
|
|
(ret =
|
|
usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
|
|
USBVISION_KEEP_BLANK | USBVISION_NOHVALID |
|
|
usbvision->Vin_Reg2_Preset)) < 0) return ret;
|
|
|
|
/* TODO: schedule timeout */
|
|
while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) & 0x01) != 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int usbvision_audio_off(struct usb_usbvision *usbvision)
|
|
{
|
|
if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) {
|
|
printk(KERN_ERR "usbvision_audio_off: can't wirte reg\n");
|
|
return -1;
|
|
}
|
|
usbvision->AudioMute = 0;
|
|
usbvision->AudioChannel = USBVISION_AUDIO_MUTE;
|
|
return 0;
|
|
}
|
|
|
|
int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel)
|
|
{
|
|
if (!usbvision->AudioMute) {
|
|
if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, AudioChannel) < 0) {
|
|
printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n");
|
|
return -1;
|
|
}
|
|
}
|
|
usbvision->AudioChannel = AudioChannel;
|
|
return 0;
|
|
}
|
|
|
|
int usbvision_setup(struct usb_usbvision *usbvision,int format)
|
|
{
|
|
usbvision_set_video_format(usbvision, format);
|
|
usbvision_set_dram_settings(usbvision);
|
|
usbvision_set_compress_params(usbvision);
|
|
usbvision_set_input(usbvision);
|
|
usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT);
|
|
usbvision_restart_isoc(usbvision);
|
|
|
|
/* cosas del PCM */
|
|
return USBVISION_IS_OPERATIONAL(usbvision);
|
|
}
|
|
|
|
int usbvision_set_alternate(struct usb_usbvision *dev)
|
|
{
|
|
int errCode, prev_alt = dev->ifaceAlt;
|
|
int i;
|
|
|
|
dev->ifaceAlt=0;
|
|
for(i=0;i< dev->num_alt; i++)
|
|
if(dev->alt_max_pkt_size[i]>dev->alt_max_pkt_size[dev->ifaceAlt])
|
|
dev->ifaceAlt=i;
|
|
|
|
if (dev->ifaceAlt != prev_alt) {
|
|
dev->isocPacketSize = dev->alt_max_pkt_size[dev->ifaceAlt];
|
|
PDEBUG(DBG_FUNC,"setting alternate %d with wMaxPacketSize=%u", dev->ifaceAlt,dev->isocPacketSize);
|
|
errCode = usb_set_interface(dev->dev, dev->iface, dev->ifaceAlt);
|
|
if (errCode < 0) {
|
|
dev_err(&dev->dev->dev,
|
|
"cannot change alternate number to %d (error=%i)\n",
|
|
dev->ifaceAlt, errCode);
|
|
return errCode;
|
|
}
|
|
}
|
|
|
|
PDEBUG(DBG_ISOC, "ISO Packet Length:%d", dev->isocPacketSize);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* usbvision_init_isoc()
|
|
*
|
|
*/
|
|
int usbvision_init_isoc(struct usb_usbvision *usbvision)
|
|
{
|
|
struct usb_device *dev = usbvision->dev;
|
|
int bufIdx, errCode, regValue;
|
|
int sb_size;
|
|
|
|
if (!USBVISION_IS_OPERATIONAL(usbvision))
|
|
return -EFAULT;
|
|
|
|
usbvision->curFrame = NULL;
|
|
scratch_reset(usbvision);
|
|
|
|
/* Alternate interface 1 is is the biggest frame size */
|
|
errCode = usbvision_set_alternate(usbvision);
|
|
if (errCode < 0) {
|
|
usbvision->last_error = errCode;
|
|
return -EBUSY;
|
|
}
|
|
sb_size = USBVISION_URB_FRAMES * usbvision->isocPacketSize;
|
|
|
|
regValue = (16 - usbvision_read_reg(usbvision,
|
|
USBVISION_ALTER_REG)) & 0x0F;
|
|
|
|
usbvision->usb_bandwidth = regValue >> 1;
|
|
PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec",
|
|
usbvision->usb_bandwidth);
|
|
|
|
|
|
|
|
/* We double buffer the Iso lists */
|
|
|
|
for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
|
|
int j, k;
|
|
struct urb *urb;
|
|
|
|
urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
|
|
if (urb == NULL) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: usb_alloc_urb() failed\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
usbvision->sbuf[bufIdx].urb = urb;
|
|
usbvision->sbuf[bufIdx].data =
|
|
usb_buffer_alloc(usbvision->dev,
|
|
sb_size,
|
|
GFP_KERNEL,
|
|
&urb->transfer_dma);
|
|
urb->dev = dev;
|
|
urb->context = usbvision;
|
|
urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp);
|
|
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
|
urb->interval = 1;
|
|
urb->transfer_buffer = usbvision->sbuf[bufIdx].data;
|
|
urb->complete = usbvision_isocIrq;
|
|
urb->number_of_packets = USBVISION_URB_FRAMES;
|
|
urb->transfer_buffer_length =
|
|
usbvision->isocPacketSize * USBVISION_URB_FRAMES;
|
|
for (j = k = 0; j < USBVISION_URB_FRAMES; j++,
|
|
k += usbvision->isocPacketSize) {
|
|
urb->iso_frame_desc[j].offset = k;
|
|
urb->iso_frame_desc[j].length =
|
|
usbvision->isocPacketSize;
|
|
}
|
|
}
|
|
|
|
/* Submit all URBs */
|
|
for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
|
|
errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb,
|
|
GFP_KERNEL);
|
|
if (errCode) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: usb_submit_urb(%d) failed: error %d\n",
|
|
__func__, bufIdx, errCode);
|
|
}
|
|
}
|
|
|
|
usbvision->streaming = Stream_Idle;
|
|
PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x",
|
|
__func__,
|
|
usbvision->video_endp);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* usbvision_stop_isoc()
|
|
*
|
|
* This procedure stops streaming and deallocates URBs. Then it
|
|
* activates zero-bandwidth alt. setting of the video interface.
|
|
*
|
|
*/
|
|
void usbvision_stop_isoc(struct usb_usbvision *usbvision)
|
|
{
|
|
int bufIdx, errCode, regValue;
|
|
int sb_size = USBVISION_URB_FRAMES * usbvision->isocPacketSize;
|
|
|
|
if ((usbvision->streaming == Stream_Off) || (usbvision->dev == NULL))
|
|
return;
|
|
|
|
/* Unschedule all of the iso td's */
|
|
for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
|
|
usb_kill_urb(usbvision->sbuf[bufIdx].urb);
|
|
if (usbvision->sbuf[bufIdx].data){
|
|
usb_buffer_free(usbvision->dev,
|
|
sb_size,
|
|
usbvision->sbuf[bufIdx].data,
|
|
usbvision->sbuf[bufIdx].urb->transfer_dma);
|
|
}
|
|
usb_free_urb(usbvision->sbuf[bufIdx].urb);
|
|
usbvision->sbuf[bufIdx].urb = NULL;
|
|
}
|
|
|
|
PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __func__);
|
|
usbvision->streaming = Stream_Off;
|
|
|
|
if (!usbvision->remove_pending) {
|
|
|
|
/* Set packet size to 0 */
|
|
usbvision->ifaceAlt=0;
|
|
errCode = usb_set_interface(usbvision->dev, usbvision->iface,
|
|
usbvision->ifaceAlt);
|
|
if (errCode < 0) {
|
|
dev_err(&usbvision->dev->dev,
|
|
"%s: usb_set_interface() failed: error %d\n",
|
|
__func__, errCode);
|
|
usbvision->last_error = errCode;
|
|
}
|
|
regValue = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
|
|
usbvision->isocPacketSize =
|
|
(regValue == 0) ? 0 : (regValue * 64) - 1;
|
|
PDEBUG(DBG_ISOC, "ISO Packet Length:%d",
|
|
usbvision->isocPacketSize);
|
|
|
|
usbvision->usb_bandwidth = regValue >> 1;
|
|
PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec",
|
|
usbvision->usb_bandwidth);
|
|
}
|
|
}
|
|
|
|
int usbvision_muxsel(struct usb_usbvision *usbvision, int channel)
|
|
{
|
|
/* inputs #0 and #3 are constant for every SAA711x. */
|
|
/* inputs #1 and #2 are variable for SAA7111 and SAA7113 */
|
|
int mode[4]= {SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3};
|
|
int audio[]= {1, 0, 0, 0};
|
|
//channel 0 is TV with audiochannel 1 (tuner mono)
|
|
//channel 1 is Composite with audio channel 0 (line in)
|
|
//channel 2 is S-Video with audio channel 0 (line in)
|
|
//channel 3 is additional video inputs to the device with audio channel 0 (line in)
|
|
|
|
RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs);
|
|
usbvision->ctl_input = channel;
|
|
|
|
// set the new channel
|
|
// Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video
|
|
// Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red
|
|
|
|
switch (usbvision_device_data[usbvision->DevModel].Codec) {
|
|
case CODEC_SAA7113:
|
|
mode[1] = SAA7115_COMPOSITE2;
|
|
if (SwitchSVideoInput) {
|
|
/* To handle problems with S-Video Input for
|
|
* some devices. Use SwitchSVideoInput
|
|
* parameter when loading the module.*/
|
|
mode[2] = SAA7115_COMPOSITE1;
|
|
}
|
|
else {
|
|
mode[2] = SAA7115_SVIDEO1;
|
|
}
|
|
break;
|
|
case CODEC_SAA7111:
|
|
default:
|
|
/* modes for saa7111 */
|
|
mode[1] = SAA7115_COMPOSITE1;
|
|
mode[2] = SAA7115_SVIDEO1;
|
|
break;
|
|
}
|
|
call_all(usbvision, video, s_routing, mode[channel], 0, 0);
|
|
usbvision_set_audio(usbvision, audio[channel]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Overrides for Emacs so that we follow Linus's tabbing style.
|
|
* ---------------------------------------------------------------------------
|
|
* Local variables:
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|