2009-03-04 01:37:50 +08:00
|
|
|
/*
|
2009-03-05 04:49:01 +08:00
|
|
|
cx231xx-core.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-05 04:49:01 +08:00
|
|
|
Based on em28xx 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 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 <linux/usb.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <media/v4l2-common.h>
|
|
|
|
|
|
|
|
#include "cx231xx.h"
|
|
|
|
#include "cx231xx-reg.h"
|
|
|
|
|
|
|
|
/* #define ENABLE_DEBUG_ISOC_FRAMES */
|
|
|
|
|
|
|
|
static unsigned int core_debug;
|
2009-03-03 17:14:34 +08:00
|
|
|
module_param(core_debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
#define cx231xx_coredbg(fmt, arg...) do {\
|
|
|
|
if (core_debug) \
|
|
|
|
printk(KERN_INFO "%s %s :"fmt, \
|
|
|
|
dev->name, __func__ , ##arg); } while (0)
|
|
|
|
|
|
|
|
static unsigned int reg_debug;
|
2009-03-03 17:14:34 +08:00
|
|
|
module_param(reg_debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
#define cx231xx_regdbg(fmt, arg...) do {\
|
|
|
|
if (reg_debug) \
|
|
|
|
printk(KERN_INFO "%s %s :"fmt, \
|
|
|
|
dev->name, __func__ , ##arg); } while (0)
|
|
|
|
|
|
|
|
static int alt = CX231XX_PINOUT;
|
|
|
|
module_param(alt, int, 0644);
|
|
|
|
MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
|
|
|
|
|
|
|
|
#define cx231xx_isocdbg(fmt, arg...) do {\
|
|
|
|
if (core_debug) \
|
|
|
|
printk(KERN_INFO "%s %s :"fmt, \
|
|
|
|
dev->name, __func__ , ##arg); } while (0)
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/*****************************************************************
|
|
|
|
* Device control list functions *
|
|
|
|
******************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
static LIST_HEAD(cx231xx_devlist);
|
|
|
|
static DEFINE_MUTEX(cx231xx_devlist_mutex);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cx231xx_realease_resources()
|
|
|
|
* unregisters the v4l2,i2c and usb devices
|
|
|
|
* called when the device gets disconected or at module unload
|
|
|
|
*/
|
|
|
|
void cx231xx_remove_from_devlist(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
mutex_lock(&cx231xx_devlist_mutex);
|
|
|
|
list_del(&dev->devlist);
|
|
|
|
mutex_unlock(&cx231xx_devlist_mutex);
|
|
|
|
};
|
|
|
|
|
|
|
|
void cx231xx_add_into_devlist(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
mutex_lock(&cx231xx_devlist_mutex);
|
|
|
|
list_add_tail(&dev->devlist, &cx231xx_devlist);
|
|
|
|
mutex_unlock(&cx231xx_devlist_mutex);
|
|
|
|
};
|
|
|
|
|
|
|
|
static LIST_HEAD(cx231xx_extension_devlist);
|
|
|
|
static DEFINE_MUTEX(cx231xx_extension_devlist_lock);
|
|
|
|
|
|
|
|
int cx231xx_register_extension(struct cx231xx_ops *ops)
|
|
|
|
{
|
|
|
|
struct cx231xx *dev = NULL;
|
|
|
|
|
|
|
|
mutex_lock(&cx231xx_devlist_mutex);
|
|
|
|
mutex_lock(&cx231xx_extension_devlist_lock);
|
|
|
|
list_add_tail(&ops->next, &cx231xx_extension_devlist);
|
|
|
|
list_for_each_entry(dev, &cx231xx_devlist, devlist) {
|
|
|
|
if (dev)
|
|
|
|
ops->init(dev);
|
|
|
|
}
|
2009-03-22 19:28:30 +08:00
|
|
|
printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name);
|
2009-03-04 01:37:50 +08:00
|
|
|
mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
|
|
mutex_unlock(&cx231xx_devlist_mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cx231xx_register_extension);
|
|
|
|
|
|
|
|
void cx231xx_unregister_extension(struct cx231xx_ops *ops)
|
|
|
|
{
|
|
|
|
struct cx231xx *dev = NULL;
|
|
|
|
|
|
|
|
mutex_lock(&cx231xx_devlist_mutex);
|
|
|
|
list_for_each_entry(dev, &cx231xx_devlist, devlist) {
|
|
|
|
if (dev)
|
|
|
|
ops->fini(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&cx231xx_extension_devlist_lock);
|
2009-03-22 19:28:30 +08:00
|
|
|
printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name);
|
2009-03-04 01:37:50 +08:00
|
|
|
list_del(&ops->next);
|
|
|
|
mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
|
|
mutex_unlock(&cx231xx_devlist_mutex);
|
|
|
|
}
|
2009-03-03 17:14:34 +08:00
|
|
|
EXPORT_SYMBOL(cx231xx_unregister_extension);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
void cx231xx_init_extension(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
struct cx231xx_ops *ops = NULL;
|
|
|
|
|
|
|
|
mutex_lock(&cx231xx_extension_devlist_lock);
|
|
|
|
if (!list_empty(&cx231xx_extension_devlist)) {
|
|
|
|
list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
|
|
|
|
if (ops->init)
|
|
|
|
ops->init(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cx231xx_close_extension(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
struct cx231xx_ops *ops = NULL;
|
|
|
|
|
|
|
|
mutex_lock(&cx231xx_extension_devlist_lock);
|
|
|
|
if (!list_empty(&cx231xx_extension_devlist)) {
|
|
|
|
list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
|
|
|
|
if (ops->fini)
|
|
|
|
ops->fini(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
|
|
}
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/****************************************************************
|
|
|
|
* U S B related functions *
|
|
|
|
*****************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_i2c_xfer_data *req_data)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int status = 0;
|
|
|
|
struct cx231xx *dev = i2c_bus->dev;
|
2009-03-05 04:49:01 +08:00
|
|
|
struct VENDOR_REQUEST_IN ven_req;
|
2009-03-03 17:14:34 +08:00
|
|
|
|
|
|
|
u8 saddr_len = 0;
|
|
|
|
u8 _i2c_period = 0;
|
|
|
|
u8 _i2c_nostop = 0;
|
|
|
|
u8 _i2c_reserve = 0;
|
|
|
|
|
|
|
|
/* Get the I2C period, nostop and reserve parameters */
|
|
|
|
_i2c_period = i2c_bus->i2c_period;
|
|
|
|
_i2c_nostop = i2c_bus->i2c_nostop;
|
|
|
|
_i2c_reserve = i2c_bus->i2c_reserve;
|
|
|
|
|
|
|
|
saddr_len = req_data->saddr_len;
|
|
|
|
|
|
|
|
/* Set wValue */
|
|
|
|
if (saddr_len == 1) /* need check saddr_len == 0 */
|
|
|
|
ven_req.wValue =
|
|
|
|
req_data->
|
|
|
|
dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
|
|
|
|
_i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
|
|
|
|
else
|
|
|
|
ven_req.wValue =
|
|
|
|
req_data->
|
|
|
|
dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
|
|
|
|
_i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
|
|
|
|
|
|
|
|
/* set channel number */
|
2009-03-05 04:49:01 +08:00
|
|
|
if (req_data->direction & I2C_M_RD) {
|
|
|
|
/* channel number, for read,spec required channel_num +4 */
|
|
|
|
ven_req.bRequest = i2c_bus->nr + 4;
|
|
|
|
} else
|
2009-03-03 17:14:34 +08:00
|
|
|
ven_req.bRequest = i2c_bus->nr; /* channel number, */
|
|
|
|
|
|
|
|
/* set index value */
|
|
|
|
switch (saddr_len) {
|
|
|
|
case 0:
|
|
|
|
ven_req.wIndex = 0; /* need check */
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
ven_req.wIndex = (req_data->saddr_dat & 0xff);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
ven_req.wIndex = req_data->saddr_dat;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set wLength value */
|
|
|
|
ven_req.wLength = req_data->buf_size;
|
|
|
|
|
|
|
|
/* set bData value */
|
|
|
|
ven_req.bData = 0;
|
|
|
|
|
|
|
|
/* set the direction */
|
|
|
|
if (req_data->direction) {
|
|
|
|
ven_req.direction = USB_DIR_IN;
|
|
|
|
memset(req_data->p_buffer, 0x00, ven_req.wLength);
|
|
|
|
} else
|
|
|
|
ven_req.direction = USB_DIR_OUT;
|
|
|
|
|
|
|
|
/* set the buffer for read / write */
|
|
|
|
ven_req.pBuff = req_data->p_buffer;
|
|
|
|
|
|
|
|
|
|
|
|
/* call common vendor command request */
|
|
|
|
status = cx231xx_send_vendor_cmd(dev, &ven_req);
|
|
|
|
if (status < 0) {
|
|
|
|
cx231xx_info
|
2009-03-05 04:49:01 +08:00
|
|
|
("UsbInterface::sendCommand, failed with status -%d\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
status);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_send_usb_command);
|
2009-03-05 04:49:01 +08:00
|
|
|
|
2009-03-04 01:37:50 +08:00
|
|
|
/*
|
|
|
|
* cx231xx_read_ctrl_reg()
|
|
|
|
* reads data from the usb device specifying bRequest and wValue
|
|
|
|
*/
|
|
|
|
int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
|
2009-03-03 17:14:34 +08:00
|
|
|
char *buf, int len)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 val = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
int ret;
|
|
|
|
int pipe = usb_rcvctrlpipe(dev->udev, 0);
|
|
|
|
|
|
|
|
if (dev->state & DEV_DISCONNECTED)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (len > URB_MAX_CTRL_SIZE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
switch (len) {
|
|
|
|
case 1:
|
|
|
|
val = ENABLE_ONE_BYTE;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
val = ENABLE_TWE_BYTE;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
val = ENABLE_THREE_BYTE;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
val = ENABLE_FOUR_BYTE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
val = 0xFF; /* invalid option */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val == 0xFF)
|
|
|
|
return -EINVAL;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (reg_debug) {
|
|
|
|
cx231xx_isocdbg("(pipe 0x%08x): "
|
2009-03-03 17:14:34 +08:00
|
|
|
"IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
|
|
|
|
pipe,
|
|
|
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
req, 0, val,
|
|
|
|
reg & 0xff, reg >> 8, len & 0xff, len >> 8);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-11 08:16:26 +08:00
|
|
|
mutex_lock(&dev->ctrl_urb_lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
ret = usb_control_msg(dev->udev, pipe, req,
|
|
|
|
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
val, reg, dev->urb_buf, len, HZ);
|
|
|
|
if (ret < 0) {
|
|
|
|
cx231xx_isocdbg(" failed!\n");
|
|
|
|
/* mutex_unlock(&dev->ctrl_urb_lock); */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len)
|
|
|
|
memcpy(buf, dev->urb_buf, len);
|
|
|
|
|
2009-03-11 08:16:26 +08:00
|
|
|
mutex_unlock(&dev->ctrl_urb_lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (reg_debug) {
|
|
|
|
int byte;
|
|
|
|
|
|
|
|
cx231xx_isocdbg("<<<");
|
|
|
|
for (byte = 0; byte < len; byte++)
|
|
|
|
cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
|
|
|
|
cx231xx_isocdbg("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
int cx231xx_send_vendor_cmd(struct cx231xx *dev,
|
|
|
|
struct VENDOR_REQUEST_IN *ven_req)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int ret;
|
2009-03-04 01:37:50 +08:00
|
|
|
int pipe = 0;
|
|
|
|
|
|
|
|
if (dev->state & DEV_DISCONNECTED)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if ((ven_req->wLength > URB_MAX_CTRL_SIZE))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (ven_req->direction)
|
|
|
|
pipe = usb_rcvctrlpipe(dev->udev, 0);
|
|
|
|
else
|
|
|
|
pipe = usb_sndctrlpipe(dev->udev, 0);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (reg_debug) {
|
|
|
|
int byte;
|
|
|
|
|
|
|
|
cx231xx_isocdbg("(pipe 0x%08x): "
|
2009-03-03 17:14:34 +08:00
|
|
|
"OUT: %02x %02x %02x %04x %04x %04x >>>",
|
|
|
|
pipe,
|
|
|
|
ven_req->
|
|
|
|
direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
ven_req->bRequest, 0, ven_req->wValue,
|
|
|
|
ven_req->wIndex, ven_req->wLength);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
for (byte = 0; byte < ven_req->wLength; byte++)
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_isocdbg(" %02x",
|
|
|
|
(unsigned char)ven_req->pBuff[byte]);
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_isocdbg("\n");
|
|
|
|
}
|
|
|
|
|
2009-03-11 08:16:26 +08:00
|
|
|
mutex_lock(&dev->ctrl_urb_lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
ret = usb_control_msg(dev->udev, pipe, ven_req->bRequest,
|
2009-03-03 17:14:34 +08:00
|
|
|
ven_req->
|
|
|
|
direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
ven_req->wValue, ven_req->wIndex, ven_req->pBuff,
|
|
|
|
ven_req->wLength, HZ);
|
2009-03-11 08:16:26 +08:00
|
|
|
mutex_unlock(&dev->ctrl_urb_lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* cx231xx_write_ctrl_reg()
|
|
|
|
* sends data to the usb device, specifying bRequest
|
|
|
|
*/
|
|
|
|
int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf,
|
2009-03-03 17:14:34 +08:00
|
|
|
int len)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 val = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
int ret;
|
|
|
|
int pipe = usb_sndctrlpipe(dev->udev, 0);
|
|
|
|
|
|
|
|
if (dev->state & DEV_DISCONNECTED)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
switch (len) {
|
|
|
|
case 1:
|
|
|
|
val = ENABLE_ONE_BYTE;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
val = ENABLE_TWE_BYTE;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
val = ENABLE_THREE_BYTE;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
val = ENABLE_FOUR_BYTE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
val = 0xFF; /* invalid option */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val == 0xFF)
|
|
|
|
return -EINVAL;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (reg_debug) {
|
|
|
|
int byte;
|
|
|
|
|
|
|
|
cx231xx_isocdbg("(pipe 0x%08x): "
|
2009-03-05 04:49:01 +08:00
|
|
|
"OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
|
|
|
|
pipe,
|
|
|
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
req, 0, val, reg & 0xff,
|
|
|
|
reg >> 8, len & 0xff, len >> 8);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
for (byte = 0; byte < len; byte++)
|
|
|
|
cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
|
|
|
|
cx231xx_isocdbg("\n");
|
|
|
|
}
|
|
|
|
|
2009-03-11 08:16:26 +08:00
|
|
|
mutex_lock(&dev->ctrl_urb_lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
memcpy(dev->urb_buf, buf, len);
|
|
|
|
ret = usb_control_msg(dev->udev, pipe, req,
|
|
|
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
val, reg, dev->urb_buf, len, HZ);
|
2009-03-11 08:16:26 +08:00
|
|
|
mutex_unlock(&dev->ctrl_urb_lock);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/****************************************************************
|
|
|
|
* USB Alternate Setting functions *
|
|
|
|
*****************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
int cx231xx_set_video_alternate(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
int errCode, prev_alt = dev->video_mode.alt;
|
|
|
|
unsigned int min_pkt_size = dev->width * 2 + 4;
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 usb_interface_index = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* When image size is bigger than a certain value,
|
|
|
|
the frame size should be increased, otherwise, only
|
|
|
|
green screen will be received.
|
|
|
|
*/
|
|
|
|
if (dev->width * 2 * dev->height > 720 * 240 * 2)
|
|
|
|
min_pkt_size *= 2;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (dev->width > 360) {
|
|
|
|
/* resolutions: 720,704,640 */
|
|
|
|
dev->video_mode.alt = 3;
|
|
|
|
} else if (dev->width > 180) {
|
|
|
|
/* resolutions: 360,352,320,240 */
|
|
|
|
dev->video_mode.alt = 2;
|
|
|
|
} else if (dev->width > 0) {
|
|
|
|
/* resolutions: 180,176,160,128,88 */
|
|
|
|
dev->video_mode.alt = 1;
|
|
|
|
} else {
|
|
|
|
/* Change to alt0 BULK to release USB bandwidth */
|
|
|
|
dev->video_mode.alt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the correct video interface Index */
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
video_index + 1;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (dev->video_mode.alt != prev_alt) {
|
|
|
|
cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
|
|
|
|
min_pkt_size, dev->video_mode.alt);
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.max_pkt_size =
|
|
|
|
dev->video_mode.alt_max_pkt_size[dev->video_mode.alt];
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.alt,
|
|
|
|
dev->video_mode.max_pkt_size);
|
|
|
|
cx231xx_info
|
2009-03-05 04:49:01 +08:00
|
|
|
(" setting alt %d with wMaxPktSize=%u , Interface = %d\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.alt, dev->video_mode.max_pkt_size,
|
|
|
|
usb_interface_index);
|
|
|
|
errCode =
|
|
|
|
usb_set_interface(dev->udev, usb_interface_index,
|
|
|
|
dev->video_mode.alt);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (errCode < 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_errdev
|
2009-03-05 04:49:01 +08:00
|
|
|
("cannot change alt number to %d (error=%i)\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.alt, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int status = 0;
|
|
|
|
u32 usb_interface_index = 0;
|
|
|
|
u32 max_pkt_size = 0;
|
|
|
|
|
|
|
|
switch (index) {
|
|
|
|
case INDEX_TS1:
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
ts1_index + 1;
|
|
|
|
dev->video_mode.alt = alt;
|
|
|
|
if (dev->ts1_mode.alt_max_pkt_size != NULL)
|
|
|
|
max_pkt_size = dev->ts1_mode.max_pkt_size =
|
|
|
|
dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt];
|
|
|
|
break;
|
|
|
|
case INDEX_TS2:
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
ts2_index + 1;
|
|
|
|
break;
|
|
|
|
case INDEX_AUDIO:
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
audio_index + 1;
|
|
|
|
dev->adev.alt = alt;
|
|
|
|
if (dev->adev.alt_max_pkt_size != NULL)
|
|
|
|
max_pkt_size = dev->adev.max_pkt_size =
|
|
|
|
dev->adev.alt_max_pkt_size[dev->adev.alt];
|
|
|
|
break;
|
|
|
|
case INDEX_VIDEO:
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
video_index + 1;
|
|
|
|
dev->video_mode.alt = alt;
|
|
|
|
if (dev->video_mode.alt_max_pkt_size != NULL)
|
|
|
|
max_pkt_size = dev->video_mode.max_pkt_size =
|
|
|
|
dev->video_mode.alt_max_pkt_size[dev->video_mode.
|
|
|
|
alt];
|
|
|
|
break;
|
|
|
|
case INDEX_VANC:
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
vanc_index + 1;
|
|
|
|
dev->vbi_mode.alt = alt;
|
|
|
|
if (dev->vbi_mode.alt_max_pkt_size != NULL)
|
|
|
|
max_pkt_size = dev->vbi_mode.max_pkt_size =
|
|
|
|
dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt];
|
|
|
|
break;
|
|
|
|
case INDEX_HANC:
|
|
|
|
usb_interface_index =
|
|
|
|
dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
|
|
hanc_index + 1;
|
|
|
|
dev->sliced_cc_mode.alt = alt;
|
|
|
|
if (dev->sliced_cc_mode.alt_max_pkt_size != NULL)
|
|
|
|
max_pkt_size = dev->sliced_cc_mode.max_pkt_size =
|
|
|
|
dev->sliced_cc_mode.alt_max_pkt_size[dev->
|
|
|
|
sliced_cc_mode.
|
|
|
|
alt];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alt > 0 && max_pkt_size == 0) {
|
|
|
|
cx231xx_errdev
|
2009-03-05 04:49:01 +08:00
|
|
|
("can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
|
|
|
|
usb_interface_index, alt);
|
2009-03-03 17:14:34 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cx231xx_info
|
|
|
|
(" setting alternate %d with wMaxPacketSize=%u , Interface = %d\n",
|
|
|
|
alt, max_pkt_size, usb_interface_index);
|
|
|
|
|
|
|
|
if (usb_interface_index > 0) {
|
|
|
|
status = usb_set_interface(dev->udev, usb_interface_index, alt);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (status < 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_errdev
|
2009-03-05 04:49:01 +08:00
|
|
|
("can't change interface %d alt no. to %d (err=%i)\n",
|
|
|
|
usb_interface_index, alt, status);
|
2009-03-04 01:37:50 +08:00
|
|
|
return status;
|
|
|
|
}
|
2009-03-03 17:14:34 +08:00
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting);
|
|
|
|
|
|
|
|
int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!gpio)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Send GPIO reset sequences specified at board entry */
|
|
|
|
while (gpio->sleep >= 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (gpio->sleep > 0)
|
|
|
|
msleep(gpio->sleep);
|
|
|
|
|
|
|
|
gpio++;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
|
|
|
|
{
|
|
|
|
if (dev->mode == set_mode)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (set_mode == CX231XX_SUSPEND) {
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Set the chip in power saving mode */
|
2009-03-04 01:37:50 +08:00
|
|
|
dev->mode = set_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resource is locked */
|
|
|
|
if (dev->mode != CX231XX_SUSPEND)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dev->mode = set_mode;
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
if (dev->mode == CX231XX_DIGITAL_MODE)
|
|
|
|
;/* Set Digital power mode */
|
|
|
|
else
|
|
|
|
;/* Set Analog Power mode */
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_set_mode);
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/*****************************************************************
|
|
|
|
* URB Streaming functions *
|
|
|
|
******************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* IRQ callback, called by URB callback
|
|
|
|
*/
|
|
|
|
static void cx231xx_irq_callback(struct urb *urb)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
|
|
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
|
|
|
int rc, i;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
switch (urb->status) {
|
|
|
|
case 0: /* success */
|
|
|
|
case -ETIMEDOUT: /* NAK */
|
|
|
|
break;
|
|
|
|
case -ECONNRESET: /* kill */
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESHUTDOWN:
|
|
|
|
return;
|
|
|
|
default: /* error */
|
|
|
|
cx231xx_isocdbg("urb completition error %d.\n", urb->status);
|
|
|
|
break;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy data from URB */
|
|
|
|
spin_lock(&dev->video_mode.slock);
|
|
|
|
rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
|
|
|
|
spin_unlock(&dev->video_mode.slock);
|
|
|
|
|
|
|
|
/* Reset urb buffers */
|
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
urb->iso_frame_desc[i].status = 0;
|
|
|
|
urb->iso_frame_desc[i].actual_length = 0;
|
|
|
|
}
|
|
|
|
urb->status = 0;
|
|
|
|
|
|
|
|
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (urb->status) {
|
|
|
|
cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
urb->status);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop and Deallocate URBs
|
|
|
|
*/
|
|
|
|
void cx231xx_uninit_isoc(struct cx231xx *dev)
|
|
|
|
{
|
|
|
|
struct urb *urb;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n");
|
|
|
|
|
|
|
|
dev->video_mode.isoc_ctl.nfields = -1;
|
|
|
|
for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
|
|
|
|
urb = dev->video_mode.isoc_ctl.urb[i];
|
|
|
|
if (urb) {
|
2009-03-03 17:14:34 +08:00
|
|
|
if (!irqs_disabled())
|
|
|
|
usb_kill_urb(urb);
|
|
|
|
else
|
|
|
|
usb_unlink_urb(urb);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
if (dev->video_mode.isoc_ctl.transfer_buffer[i]) {
|
|
|
|
usb_buffer_free(dev->udev,
|
2009-03-03 17:14:34 +08:00
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dev->video_mode.isoc_ctl.
|
|
|
|
transfer_buffer[i],
|
|
|
|
urb->transfer_dma);
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
usb_free_urb(urb);
|
|
|
|
dev->video_mode.isoc_ctl.urb[i] = NULL;
|
|
|
|
}
|
|
|
|
dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(dev->video_mode.isoc_ctl.urb);
|
|
|
|
kfree(dev->video_mode.isoc_ctl.transfer_buffer);
|
|
|
|
|
|
|
|
dev->video_mode.isoc_ctl.urb = NULL;
|
|
|
|
dev->video_mode.isoc_ctl.transfer_buffer = NULL;
|
|
|
|
dev->video_mode.isoc_ctl.num_bufs = 0;
|
|
|
|
|
|
|
|
cx231xx_capture_start(dev, 0, Raw_Video);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate URBs and start IRQ
|
|
|
|
*/
|
|
|
|
int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
|
2009-03-03 17:14:34 +08:00
|
|
|
int num_bufs, int max_pkt_size,
|
2009-03-05 04:49:01 +08:00
|
|
|
int (*isoc_copy) (struct cx231xx *dev, struct urb *urb))
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
|
|
|
struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
|
|
|
|
int i;
|
|
|
|
int sb_size, pipe;
|
|
|
|
struct urb *urb;
|
|
|
|
int j, k;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
cx231xx_isocdbg("cx231xx: called cx231xx_prepare_isoc\n");
|
|
|
|
|
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
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_info("Setting Video mux to %d\n", dev->video_input);
|
|
|
|
video_mux(dev, dev->video_input);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* De-allocates all pending stuff */
|
|
|
|
cx231xx_uninit_isoc(dev);
|
|
|
|
|
|
|
|
dev->video_mode.isoc_ctl.isoc_copy = isoc_copy;
|
|
|
|
dev->video_mode.isoc_ctl.num_bufs = num_bufs;
|
2009-03-03 17:14:34 +08:00
|
|
|
dma_q->pos = 0;
|
|
|
|
dma_q->is_partial_line = 0;
|
|
|
|
dma_q->last_sav = 0;
|
|
|
|
dma_q->current_field = -1;
|
|
|
|
dma_q->field1_done = 0;
|
|
|
|
dma_q->lines_per_field = dev->height / 2;
|
|
|
|
dma_q->bytes_left_in_line = dev->width << 1;
|
|
|
|
dma_q->lines_completed = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
dma_q->partial_buf[i] = 0;
|
|
|
|
|
|
|
|
dev->video_mode.isoc_ctl.urb =
|
|
|
|
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (!dev->video_mode.isoc_ctl.urb) {
|
|
|
|
cx231xx_errdev("cannot alloc memory for usb buffers\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.isoc_ctl.transfer_buffer =
|
|
|
|
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (!dev->video_mode.isoc_ctl.transfer_buffer) {
|
|
|
|
cx231xx_errdev("cannot allocate memory for usbtransfer\n");
|
|
|
|
kfree(dev->video_mode.isoc_ctl.urb);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size;
|
|
|
|
dev->video_mode.isoc_ctl.buf = NULL;
|
|
|
|
|
|
|
|
sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size;
|
|
|
|
|
|
|
|
/* allocate urbs and transfer buffers */
|
|
|
|
for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
|
|
|
|
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
|
|
|
|
if (!urb) {
|
|
|
|
cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i);
|
|
|
|
cx231xx_uninit_isoc(dev);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
dev->video_mode.isoc_ctl.urb[i] = urb;
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.isoc_ctl.transfer_buffer[i] =
|
|
|
|
usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL,
|
|
|
|
&urb->transfer_dma);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) {
|
|
|
|
cx231xx_err("unable to allocate %i bytes for transfer"
|
2009-03-03 17:14:34 +08:00
|
|
|
" buffer %i%s\n",
|
|
|
|
sb_size, i,
|
2009-03-05 04:49:01 +08:00
|
|
|
in_interrupt() ? " while in int" : "");
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_uninit_isoc(dev);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size);
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
pipe =
|
|
|
|
usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
usb_fill_int_urb(urb, dev->udev, pipe,
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.isoc_ctl.transfer_buffer[i],
|
|
|
|
sb_size, cx231xx_irq_callback, dma_q, 1);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
urb->number_of_packets = max_packets;
|
|
|
|
urb->transfer_flags = URB_ISO_ASAP;
|
|
|
|
|
|
|
|
k = 0;
|
|
|
|
for (j = 0; j < max_packets; j++) {
|
|
|
|
urb->iso_frame_desc[j].offset = k;
|
|
|
|
urb->iso_frame_desc[j].length =
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->video_mode.isoc_ctl.max_pkt_size;
|
2009-03-04 01:37:50 +08:00
|
|
|
k += dev->video_mode.isoc_ctl.max_pkt_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
init_waitqueue_head(&dma_q->wq);
|
|
|
|
|
|
|
|
/* submit urbs and enables IRQ */
|
|
|
|
for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
|
2009-03-03 17:14:34 +08:00
|
|
|
rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i],
|
|
|
|
GFP_ATOMIC);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (rc) {
|
|
|
|
cx231xx_err("submit of urb %i failed (error=%i)\n", i,
|
2009-03-03 17:14:34 +08:00
|
|
|
rc);
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_uninit_isoc(dev);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_capture_start(dev, 1, Raw_Video);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_init_isoc);
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/*****************************************************************
|
|
|
|
* Device Init/UnInit functions *
|
|
|
|
******************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
int cx231xx_dev_init(struct cx231xx *dev)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int errCode = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Initialize I2C bus */
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* External Master 1 Bus */
|
|
|
|
dev->i2c_bus[0].nr = 0;
|
|
|
|
dev->i2c_bus[0].dev = dev;
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->i2c_bus[0].i2c_period = I2C_SPEED_1M; /* 1MHz */
|
|
|
|
dev->i2c_bus[0].i2c_nostop = 0;
|
|
|
|
dev->i2c_bus[0].i2c_reserve = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* External Master 2 Bus */
|
|
|
|
dev->i2c_bus[1].nr = 1;
|
|
|
|
dev->i2c_bus[1].dev = dev;
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->i2c_bus[1].i2c_period = I2C_SPEED_1M; /* 1MHz */
|
|
|
|
dev->i2c_bus[1].i2c_nostop = 0;
|
|
|
|
dev->i2c_bus[1].i2c_reserve = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
/* Internal Master 3 Bus */
|
|
|
|
dev->i2c_bus[2].nr = 2;
|
|
|
|
dev->i2c_bus[2].dev = dev;
|
2009-03-03 17:14:34 +08:00
|
|
|
dev->i2c_bus[2].i2c_period = I2C_SPEED_400K; /* 400kHz */
|
|
|
|
dev->i2c_bus[2].i2c_nostop = 0;
|
|
|
|
dev->i2c_bus[2].i2c_reserve = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* register I2C buses */
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_i2c_register(&dev->i2c_bus[0]);
|
|
|
|
cx231xx_i2c_register(&dev->i2c_bus[1]);
|
|
|
|
cx231xx_i2c_register(&dev->i2c_bus[2]);
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* init hardware */
|
2009-03-05 04:49:01 +08:00
|
|
|
/* Note : with out calling set power mode function,
|
2009-03-22 09:00:20 +08:00
|
|
|
afe can not be set up correctly */
|
2009-03-03 17:14:34 +08:00
|
|
|
errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
|
|
|
|
if (errCode < 0) {
|
|
|
|
cx231xx_errdev
|
2009-03-05 04:49:01 +08:00
|
|
|
("%s: Failed to set Power - errCode [%d]!\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* initialize Colibri block */
|
2009-03-22 09:00:20 +08:00
|
|
|
errCode = cx231xx_afe_init_super_block(dev, 0x23c);
|
2009-03-04 01:37:50 +08:00
|
|
|
if (errCode < 0) {
|
2009-03-03 17:14:34 +08:00
|
|
|
cx231xx_errdev
|
2009-03-22 09:00:20 +08:00
|
|
|
("%s: cx231xx_afe init super block - errCode [%d]!\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
2009-03-22 09:00:20 +08:00
|
|
|
errCode = cx231xx_afe_init_channels(dev);
|
2009-03-03 17:14:34 +08:00
|
|
|
if (errCode < 0) {
|
|
|
|
cx231xx_errdev
|
2009-03-22 09:00:20 +08:00
|
|
|
("%s: cx231xx_afe init channels - errCode [%d]!\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Set DIF in By pass mode */
|
|
|
|
errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
|
|
|
|
if (errCode < 0) {
|
|
|
|
cx231xx_errdev
|
|
|
|
("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n",
|
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2009-03-22 09:00:20 +08:00
|
|
|
/* I2S block related functions */
|
|
|
|
errCode = cx231xx_i2s_blk_initialize(dev);
|
2009-03-03 17:14:34 +08:00
|
|
|
if (errCode < 0) {
|
|
|
|
cx231xx_errdev
|
2009-03-22 09:00:20 +08:00
|
|
|
("%s: cx231xx_i2s block initialize - errCode [%d]!\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* init control pins */
|
|
|
|
errCode = cx231xx_init_ctrl_pin_status(dev);
|
|
|
|
if (errCode < 0) {
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set AGC mode to Analog */
|
|
|
|
errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
|
|
|
|
if (errCode < 0) {
|
|
|
|
cx231xx_errdev
|
|
|
|
("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
|
|
|
|
__func__, errCode);
|
2009-03-04 01:37:50 +08:00
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set all alternate settings to zero initially */
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
|
|
|
|
if (dev->board.has_dvb)
|
|
|
|
cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set the I2C master port to 3 on channel 1 */
|
|
|
|
errCode = cx231xx_enable_i2c_for_tuner(dev, I2C_3);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
return errCode;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_dev_init);
|
|
|
|
|
|
|
|
void cx231xx_dev_uninit(struct cx231xx *dev)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
/* Un Initialize I2C bus */
|
2009-03-04 01:37:50 +08:00
|
|
|
cx231xx_i2c_unregister(&dev->i2c_bus[2]);
|
|
|
|
cx231xx_i2c_unregister(&dev->i2c_bus[1]);
|
|
|
|
cx231xx_i2c_unregister(&dev->i2c_bus[0]);
|
|
|
|
}
|
2009-03-03 17:14:34 +08:00
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_dev_uninit);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/*****************************************************************
|
|
|
|
* G P I O related functions *
|
|
|
|
******************************************************************/
|
2009-03-03 17:14:34 +08:00
|
|
|
int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val,
|
|
|
|
u8 len, u8 request, u8 direction)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int status = 0;
|
2009-03-05 04:49:01 +08:00
|
|
|
struct VENDOR_REQUEST_IN ven_req;
|
2009-03-03 17:14:34 +08:00
|
|
|
|
|
|
|
/* Set wValue */
|
|
|
|
ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff);
|
|
|
|
|
|
|
|
/* set request */
|
|
|
|
if (!request) {
|
|
|
|
if (direction)
|
|
|
|
ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */
|
|
|
|
else
|
|
|
|
ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */
|
|
|
|
} else {
|
|
|
|
if (direction)
|
|
|
|
ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */
|
|
|
|
else
|
|
|
|
ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set index value */
|
|
|
|
ven_req.wIndex = (u16) (gpio_bit & 0xffff);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set wLength value */
|
|
|
|
ven_req.wLength = len;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set bData value */
|
|
|
|
ven_req.bData = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
/* set the buffer for read / write */
|
|
|
|
ven_req.pBuff = gpio_val;
|
|
|
|
|
|
|
|
/* set the direction */
|
|
|
|
if (direction) {
|
|
|
|
ven_req.direction = USB_DIR_IN;
|
|
|
|
memset(ven_req.pBuff, 0x00, ven_req.wLength);
|
|
|
|
} else
|
|
|
|
ven_req.direction = USB_DIR_OUT;
|
|
|
|
|
|
|
|
|
|
|
|
/* call common vendor command request */
|
|
|
|
status = cx231xx_send_vendor_cmd(dev, &ven_req);
|
|
|
|
if (status < 0) {
|
|
|
|
cx231xx_info
|
2009-03-05 04:49:01 +08:00
|
|
|
("UsbInterface::sendCommand, failed with status -%d\n",
|
2009-03-03 17:14:34 +08:00
|
|
|
status);
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd);
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/*****************************************************************
|
|
|
|
* C O N T R O L - Register R E A D / W R I T E functions *
|
|
|
|
*****************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
|
|
|
|
u32 tmp = 0;
|
|
|
|
int status = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
status =
|
|
|
|
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4);
|
|
|
|
if (status < 0)
|
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
tmp = *((u32 *) value);
|
|
|
|
tmp |= mode;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
value[0] = (u8) tmp;
|
|
|
|
value[1] = (u8) (tmp >> 8);
|
|
|
|
value[2] = (u8) (tmp >> 16);
|
|
|
|
value[3] = (u8) (tmp >> 24);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
status =
|
|
|
|
cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
/*****************************************************************
|
|
|
|
* I 2 C Internal C O N T R O L functions *
|
|
|
|
*****************************************************************/
|
2009-03-04 01:37:50 +08:00
|
|
|
int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
|
2009-03-05 04:49:01 +08:00
|
|
|
u8 saddr_len, u32 *data, u8 data_len)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int status = 0;
|
|
|
|
struct cx231xx_i2c_xfer_data req_data;
|
|
|
|
u8 value[4] = { 0, 0, 0, 0 };
|
|
|
|
|
|
|
|
if (saddr_len == 0)
|
|
|
|
saddr = 0;
|
|
|
|
else if (saddr_len == 0)
|
|
|
|
saddr &= 0xff;
|
|
|
|
|
|
|
|
/* prepare xfer_data struct */
|
|
|
|
req_data.dev_addr = dev_addr >> 1;
|
|
|
|
req_data.direction = I2C_M_RD;
|
|
|
|
req_data.saddr_len = saddr_len;
|
|
|
|
req_data.saddr_dat = saddr;
|
|
|
|
req_data.buf_size = data_len;
|
|
|
|
req_data.p_buffer = (u8 *) value;
|
|
|
|
|
|
|
|
/* usb send command */
|
|
|
|
status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
|
|
|
|
|
|
|
|
if (status >= 0) {
|
|
|
|
/* Copy the data read back to main buffer */
|
|
|
|
if (data_len == 1)
|
|
|
|
*data = value[0];
|
|
|
|
else
|
|
|
|
*data =
|
|
|
|
value[0] | value[1] << 8 | value[2] << 16 | value[3]
|
|
|
|
<< 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
|
2009-03-03 17:14:34 +08:00
|
|
|
u8 saddr_len, u32 data, u8 data_len)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int status = 0;
|
|
|
|
u8 value[4] = { 0, 0, 0, 0 };
|
|
|
|
struct cx231xx_i2c_xfer_data req_data;
|
|
|
|
|
|
|
|
value[0] = (u8) data;
|
|
|
|
value[1] = (u8) (data >> 8);
|
|
|
|
value[2] = (u8) (data >> 16);
|
|
|
|
value[3] = (u8) (data >> 24);
|
|
|
|
|
|
|
|
if (saddr_len == 0)
|
|
|
|
saddr = 0;
|
|
|
|
else if (saddr_len == 0)
|
|
|
|
saddr &= 0xff;
|
|
|
|
|
|
|
|
/* prepare xfer_data struct */
|
|
|
|
req_data.dev_addr = dev_addr >> 1;
|
|
|
|
req_data.direction = 0;
|
|
|
|
req_data.saddr_len = saddr_len;
|
|
|
|
req_data.saddr_dat = saddr;
|
|
|
|
req_data.buf_size = data_len;
|
|
|
|
req_data.p_buffer = value;
|
|
|
|
|
|
|
|
/* usb send command */
|
|
|
|
status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
|
|
|
|
|
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
|
|
|
|
u16 register_address, u8 bit_start, u8 bit_end,
|
|
|
|
u32 value)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
int status = 0;
|
|
|
|
u32 tmp;
|
|
|
|
u32 mask = 0;
|
|
|
|
int i;
|
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
if (bit_start > (size - 1) || bit_end > (size - 1))
|
2009-03-03 17:14:34 +08:00
|
|
|
return -1;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (size == 8) {
|
|
|
|
status =
|
|
|
|
cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
|
|
|
|
&tmp, 1);
|
|
|
|
} else {
|
|
|
|
status =
|
|
|
|
cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
|
|
|
|
&tmp, 4);
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
if (status < 0)
|
2009-03-03 17:14:34 +08:00
|
|
|
return status;
|
|
|
|
|
|
|
|
mask = 1 << bit_end;
|
2009-03-05 04:49:01 +08:00
|
|
|
for (i = bit_end; i > bit_start && i > 0; i--)
|
2009-03-03 17:14:34 +08:00
|
|
|
mask = mask + (1 << (i - 1));
|
|
|
|
|
|
|
|
value <<= bit_start;
|
|
|
|
|
|
|
|
if (size == 8) {
|
|
|
|
tmp &= ~mask;
|
|
|
|
tmp |= value;
|
|
|
|
tmp &= 0xff;
|
|
|
|
status =
|
|
|
|
cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
|
|
|
|
tmp, 1);
|
|
|
|
} else {
|
|
|
|
tmp &= ~mask;
|
|
|
|
tmp |= value;
|
|
|
|
status =
|
|
|
|
cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
|
|
|
|
tmp, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
2009-03-04 01:37:50 +08:00
|
|
|
|
|
|
|
int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
|
2009-03-03 17:14:34 +08:00
|
|
|
u16 saddr, u32 mask, u32 value)
|
2009-03-04 01:37:50 +08:00
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 temp;
|
|
|
|
int status = 0;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
if (status < 0)
|
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
temp &= ~mask;
|
|
|
|
temp |= value;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4);
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return status;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 cx231xx_set_field(u32 field_mask, u32 data)
|
|
|
|
{
|
2009-03-03 17:14:34 +08:00
|
|
|
u32 temp;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-05 04:49:01 +08:00
|
|
|
for (temp = field_mask; (temp & 1) == 0; temp >>= 1)
|
2009-03-03 17:14:34 +08:00
|
|
|
data <<= 1;
|
2009-03-04 01:37:50 +08:00
|
|
|
|
2009-03-03 17:14:34 +08:00
|
|
|
return data;
|
2009-03-04 01:37:50 +08:00
|
|
|
}
|