Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (120 commits)
  cx231xx: Convert to snd_card_create()
  V4L/DVB (11440): PWC: fix build error when CONFIG_INPUT=m
  V4L/DVB (11439): UVC: uvc_status_cleanup(): undefined reference to `input_unregister_device'
  V4L/DVB (11438): au0828: fix Kconfig dependance
  V4L/DVB (11437): pvrusb2: Drop client_register/unregister stubs
  V4L/DVB (11436): radio-mr800: convert to to v4l2_device
  V4L/DVB (11435): dsbr100 radio: convert to to v4l2_device
  V4L/DVB: zr364xx: remove unused #include <version.h>
  V4L/DVB: usbvision: remove unused #include <version.h>
  V4L/DVB (11427): gspca - m5602: Minor cleanups
  V4L/DVB (11426): gspca - m5602: Don't touch hflip/vflip register on Read/Modify/Write
  V4L/DVB (11425): gspca - m5602: Move the vflip quirk to probe stage.
  V4L/DVB (11424): gspca - m5602-ov9650: Use the local ctrl cache. Adjust image on vflip.
  V4L/DVB (11423): gspca - m5602-ov9650: Add a disconnect hook, setup a ctrl cache ctrl.
  V4L/DVB (11422): gspca - m5602-ov9650: Replace a magic constant with a define
  V4L/DVB (11421): gspca - m5602-ov9650: Synthesize modesetting.
  V4L/DVB (11420): gspca - m5602: Improve error handling in the ov9650 driver
  V4L/DVB (11419): gspca - m5602-ov9650: Don't read exposure data from COM1.
  V4L/DVB (11418): gspca - m5602-ov9650: Auto white balancing is on by default
  V4L/DVB (11417): gspca - m5602-ov9650: Autogain is on by default
  ...
This commit is contained in:
Linus Torvalds 2009-04-06 18:08:58 -07:00
commit 4ef4327b30
200 changed files with 18703 additions and 3344 deletions

View File

@ -0,0 +1,125 @@
PXA-Camera Host Driver
======================
Constraints
-----------
a) Image size for YUV422P format
All YUV422P images are enforced to have width x height % 16 = 0.
This is due to DMA constraints, which transfers only planes of 8 byte
multiples.
Global video workflow
---------------------
a) QCI stopped
Initialy, the QCI interface is stopped.
When a buffer is queued (pxa_videobuf_ops->buf_queue), the QCI starts.
b) QCI started
More buffers can be queued while the QCI is started without halting the
capture. The new buffers are "appended" at the tail of the DMA chain, and
smoothly captured one frame after the other.
Once a buffer is filled in the QCI interface, it is marked as "DONE" and
removed from the active buffers list. It can be then requeud or dequeued by
userland application.
Once the last buffer is filled in, the QCI interface stops.
DMA usage
---------
a) DMA flow
- first buffer queued for capture
Once a first buffer is queued for capture, the QCI is started, but data
transfer is not started. On "End Of Frame" interrupt, the irq handler
starts the DMA chain.
- capture of one videobuffer
The DMA chain starts transfering data into videobuffer RAM pages.
When all pages are transfered, the DMA irq is raised on "ENDINTR" status
- finishing one videobuffer
The DMA irq handler marks the videobuffer as "done", and removes it from
the active running queue
Meanwhile, the next videobuffer (if there is one), is transfered by DMA
- finishing the last videobuffer
On the DMA irq of the last videobuffer, the QCI is stopped.
b) DMA prepared buffer will have this structure
+------------+-----+---------------+-----------------+
| desc-sg[0] | ... | desc-sg[last] | finisher/linker |
+------------+-----+---------------+-----------------+
This structure is pointed by dma->sg_cpu.
The descriptors are used as follows :
- desc-sg[i]: i-th descriptor, transfering the i-th sg
element to the video buffer scatter gather
- finisher: has ddadr=DADDR_STOP, dcmd=ENDIRQEN
- linker: has ddadr= desc-sg[0] of next video buffer, dcmd=0
For the next schema, let's assume d0=desc-sg[0] .. dN=desc-sg[N],
"f" stands for finisher and "l" for linker.
A typical running chain is :
Videobuffer 1 Videobuffer 2
+---------+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+---+
| |
+----+
After the chaining is finished, the chain looks like :
Videobuffer 1 Videobuffer 2 Videobuffer 3
+---------+----+---+ +----+----+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-|-+ ^----+----+----+---+
| | | |
+----+ +----+
new_link
c) DMA hot chaining timeslice issue
As DMA chaining is done while DMA _is_ running, the linking may be done
while the DMA jumps from one Videobuffer to another. On the schema, that
would be a problem if the following sequence is encountered :
- DMA chain is Videobuffer1 + Videobuffer2
- pxa_videobuf_queue() is called to queue Videobuffer3
- DMA controller finishes Videobuffer2, and DMA stops
=>
Videobuffer 1 Videobuffer 2
+---------+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-^-+
| | |
+----+ +-- DMA DDADR loads DDADR_STOP
- pxa_dma_add_tail_buf() is called, the Videobuffer2 "finisher" is
replaced by a "linker" to Videobuffer3 (creation of new_link)
- pxa_videobuf_queue() finishes
- the DMA irq handler is called, which terminates Videobuffer2
- Videobuffer3 capture is not scheduled on DMA chain (as it stopped !!!)
Videobuffer 1 Videobuffer 2 Videobuffer 3
+---------+----+---+ +----+----+----+---+ +----+----+----+---+
| d0 | .. | dN | l | | d0 | .. | dN | l | | d0 | .. | dN | f |
+---------+----+-|-+ ^----+----+----+-|-+ ^----+----+----+---+
| | | |
+----+ +----+
new_link
DMA DDADR still is DDADR_STOP
- pxa_camera_check_link_miss() is called
This checks if the DMA is finished and a buffer is still on the
pcdev->capture list. If that's the case, the capture will be restarted,
and Videobuffer3 is scheduled on DMA chain.
- the DMA irq handler finishes
Note: if DMA stops just after pxa_camera_check_link_miss() reads DDADR()
value, we have the guarantee that the DMA irq handler will be called back
when the DMA will finish the buffer, and pxa_camera_check_link_miss() will
be called again, to reschedule Videobuffer3.
--
Author: Robert Jarzmik <robert.jarzmik@free.fr>

View File

@ -90,7 +90,7 @@ up before calling v4l2_device_register then it will be untouched. If dev is
NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register.
The first 'dev' argument is normally the struct device pointer of a pci_dev,
usb_device or platform_device. It is rare for dev to be NULL, but it happens
usb_interface or platform_device. It is rare for dev to be NULL, but it happens
with ISA devices or when one device creates multiple PCI devices, thus making
it impossible to associate v4l2_dev with a particular parent.
@ -351,17 +351,6 @@ And this to go from an i2c_client to a v4l2_subdev struct:
struct v4l2_subdev *sd = i2c_get_clientdata(client);
Finally you need to make a command function to make driver->command()
call the right subdev_ops functions:
static int subdev_command(struct i2c_client *client, unsigned cmd, void *arg)
{
return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
If driver->command is never used then you can leave this out. Eventually the
driver->command usage should be removed from v4l.
Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback
is called. This will unregister the sub-device from the bridge driver. It is
safe to call this even if the sub-device was never registered.
@ -375,14 +364,12 @@ from the remove() callback ensures that this is always done correctly.
The bridge driver also has some helper functions it can use:
struct v4l2_subdev *sd = v4l2_i2c_new_subdev(adapter, "module_foo", "chipid", 0x36);
struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
"module_foo", "chipid", 0x36);
This loads the given module (can be NULL if no module needs to be loaded) and
calls i2c_new_device() with the given i2c_adapter and chip/address arguments.
If all goes well, then it registers the subdev with the v4l2_device. It gets
the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure
to call i2c_set_adapdata(adapter, v4l2_device) when you setup the i2c_adapter
in your driver.
If all goes well, then it registers the subdev with the v4l2_device.
You can also use v4l2_i2c_new_probed_subdev() which is very similar to
v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses

View File

@ -1544,7 +1544,6 @@ S: Maintained
DVB SUBSYSTEM AND DRIVERS
P: LinuxTV.org Project
M: linux-media@vger.kernel.org
L: linux-dvb@linuxtv.org (subscription required)
W: http://linuxtv.org/
T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/linux-2.6.git
S: Maintained

View File

@ -6,6 +6,9 @@
obj-y += generic.o clock.o devices.o
# Support for CMOS sensor interface
obj-$(CONFIG_MX1_VIDEO) += ksym_mx1.o mx1_camera_fiq.o
# Specific board support
obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o
obj-$(CONFIG_MACH_SCB9328) += scb9328.o

View File

@ -44,7 +44,7 @@ static struct resource imx_csi_resources[] = {
static u64 imx_csi_dmamask = 0xffffffffUL;
struct platform_device imx_csi_device = {
.name = "imx-csi",
.name = "mx1-camera",
.id = 0, /* This is used to put cameras on this interface */
.dev = {
.dma_mask = &imx_csi_dmamask,

View File

@ -0,0 +1,18 @@
/*
* Exported ksyms of ARCH_MX1
*
* Copyright (C) 2008, Darius Augulis <augulis.darius@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <mach/mx1_camera.h>
/* IMX camera FIQ handler */
EXPORT_SYMBOL(mx1_camera_sof_fiq_start);
EXPORT_SYMBOL(mx1_camera_sof_fiq_end);

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2008 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
*
* Based on linux/arch/arm/lib/floppydma.S
* Copyright (C) 1995, 1996 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
.text
.global mx1_camera_sof_fiq_end
.global mx1_camera_sof_fiq_start
mx1_camera_sof_fiq_start:
@ enable dma
ldr r12, [r9]
orr r12, r12, #0x00000001
str r12, [r9]
@ unmask DMA interrupt
ldr r12, [r8]
bic r12, r12, r13
str r12, [r8]
@ disable SOF interrupt
ldr r12, [r10]
bic r12, r12, #0x00010000
str r12, [r10]
@ clear SOF flag
mov r12, #0x00010000
str r12, [r11]
@ return from FIQ
subs pc, lr, #4
mx1_camera_sof_fiq_end:

View File

@ -533,7 +533,7 @@ static struct clk_lookup lookups[] __initdata = {
_REGISTER_CLOCK(NULL, "kpp", kpp_clk)
_REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk1)
_REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk2)
_REGISTER_CLOCK("mx3-camera.0", "csi", csi_clk)
_REGISTER_CLOCK("mx3-camera.0", NULL, csi_clk)
_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)

View File

@ -24,4 +24,12 @@
#define PHYS_OFFSET UL(0x80000000)
#endif
#if defined(CONFIG_MX1_VIDEO)
/*
* Increase size of DMA-consistent memory region.
* This is required for i.MX camera driver to capture at least four VGA frames.
*/
#define CONSISTENT_DMA_SIZE SZ_4M
#endif /* CONFIG_MX1_VIDEO */
#endif /* __ASM_ARCH_MXC_MEMORY_H__ */

View File

@ -0,0 +1,35 @@
/*
* mx1_camera.h - i.MX1/i.MXL camera driver header file
*
* Copyright (c) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
* Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
*
* Based on PXA camera.h file:
* Copyright (C) 2003, Intel Corporation
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARCH_CAMERA_H_
#define __ASM_ARCH_CAMERA_H_
#define MX1_CAMERA_DATA_HIGH 1
#define MX1_CAMERA_PCLK_RISING 2
#define MX1_CAMERA_VSYNC_HIGH 4
extern unsigned char mx1_camera_sof_fiq_start, mx1_camera_sof_fiq_end;
/**
* struct mx1_camera_pdata - i.MX1/i.MXL camera platform data
* @mclk_10khz: master clock frequency in 10kHz units
* @flags: MX1 camera platform flags
*/
struct mx1_camera_pdata {
unsigned long mclk_10khz;
unsigned long flags;
};
#endif /* __ASM_ARCH_CAMERA_H_ */

View File

@ -294,7 +294,7 @@ config DVB_USB_DTV5100
config DVB_USB_AF9015
tristate "Afatech AF9015 DVB-T USB2.0 support"
depends on DVB_USB && EXPERIMENTAL
depends on DVB_USB
select DVB_AF9013
select DVB_PLL if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
@ -309,6 +309,6 @@ config DVB_USB_CE6230
tristate "Intel CE6230 DVB-T USB2.0 support"
depends on DVB_USB && EXPERIMENTAL
select DVB_ZL10353
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver

View File

@ -782,17 +782,14 @@ static int af9015_read_config(struct usb_device *udev)
ARRAY_SIZE(af9015_ir_table_leadtek);
break;
case USB_VID_VISIONPLUS:
if (udev->descriptor.idProduct ==
cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) {
af9015_properties[i].rc_key_map =
af9015_rc_keys_twinhan;
af9015_properties[i].rc_key_map_size =
ARRAY_SIZE(af9015_rc_keys_twinhan);
af9015_config.ir_table =
af9015_ir_table_twinhan;
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_twinhan);
}
af9015_properties[i].rc_key_map =
af9015_rc_keys_twinhan;
af9015_properties[i].rc_key_map_size =
ARRAY_SIZE(af9015_rc_keys_twinhan);
af9015_config.ir_table =
af9015_ir_table_twinhan;
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_twinhan);
break;
case USB_VID_KWORLD_2:
/* TODO: use correct rc keys */
@ -833,6 +830,16 @@ static int af9015_read_config(struct usb_device *udev)
af9015_ir_table_msi;
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_msi);
} else if (udev->descriptor.idProduct ==
cpu_to_le16(USB_PID_TREKSTOR_DVBT)) {
af9015_properties[i].rc_key_map =
af9015_rc_keys_trekstor;
af9015_properties[i].rc_key_map_size =
ARRAY_SIZE(af9015_rc_keys_trekstor);
af9015_config.ir_table =
af9015_ir_table_trekstor;
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_trekstor);
}
break;
case USB_VID_AVERMEDIA:
@ -981,6 +988,21 @@ error:
if (ret)
err("eeprom read failed:%d", ret);
/* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM
content :-( Override some wrong values here. */
if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_AVERMEDIA &&
le16_to_cpu(udev->descriptor.idProduct) == USB_PID_AVERMEDIA_A850) {
deb_info("%s: AverMedia A850: overriding config\n", __func__);
/* disable dual mode */
af9015_config.dual_mode = 0;
/* disable 2nd adapter */
for (i = 0; i < af9015_properties_count; i++)
af9015_properties[i].num_adapters = 1;
/* set correct IF */
af9015_af9013_config[0].tuner_if = 4570;
}
return ret;
}
@ -1237,6 +1259,9 @@ static struct usb_device_id af9015_usb_table[] = {
/* 15 */{USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)},
{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)},
{USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)},
{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@ -1401,7 +1426,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.i2c_algo = &af9015_i2c_algo,
.num_device_descs = 7,
.num_device_descs = 9,
.devices = {
{
.name = "Xtensions XD-380",
@ -1437,7 +1462,19 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.name = "KWorld USB DVB-T TV Stick II " \
"(VS-DVB-T 395U)",
.cold_ids = {&af9015_usb_table[16],
&af9015_usb_table[17], NULL},
&af9015_usb_table[17],
&af9015_usb_table[18], NULL},
.warm_ids = {NULL},
},
{
.name = "TrekStor DVB-T USB Stick",
.cold_ids = {&af9015_usb_table[19], NULL},
.warm_ids = {NULL},
},
{
.name = "AverMedia AVerTV Volar Black HD " \
"(A850)",
.cold_ids = {&af9015_usb_table[20], NULL},
.warm_ids = {NULL},
},
}

View File

@ -64,14 +64,6 @@
#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
#define AF9015_GPIO_ON (1 << 0)
#define AF9015_GPIO_EN (1 << 1)
#define AF9015_GPIO_O (1 << 2)
#define AF9015_GPIO_I (1 << 3)
#define AF9015_GPIO_TUNER_ON (AF9015_GPIO_ON|AF9015_GPIO_EN)
#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O)
struct req_t {
u8 cmd; /* [0] */
/* seq */ /* [1] */
@ -120,11 +112,11 @@ struct af9015_config {
enum af9015_remote {
AF9015_REMOTE_NONE = 0,
AF9015_REMOTE_A_LINK_DTU_M,
/* 1 */ AF9015_REMOTE_A_LINK_DTU_M,
AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
AF9015_REMOTE_MYGICTV_U718,
AF9015_REMOTE_DIGITTRADE_DVB_T,
AF9015_REMOTE_AVERMEDIA_KS,
/* 5 */ AF9015_REMOTE_AVERMEDIA_KS,
};
/* Leadtek WinFast DTV Dongle Gold */
@ -691,4 +683,67 @@ static u8 af9015_ir_table_digittrade[] = {
0x00, 0xff, 0x1d, 0xe2, 0x40, 0x00, 0x00,
};
/* TREKSTOR DVB-T USB Stick */
static struct dvb_usb_rc_key af9015_rc_keys_trekstor[] = {
{ 0x07, 0x04, KEY_AGAIN }, /* Home */
{ 0x07, 0x05, KEY_MUTE }, /* Mute */
{ 0x07, 0x06, KEY_UP }, /* Up */
{ 0x07, 0x07, KEY_DOWN }, /* Down */
{ 0x07, 0x09, KEY_RIGHT }, /* Right */
{ 0x07, 0x0a, KEY_ENTER }, /* OK */
{ 0x07, 0x0b, KEY_FASTFORWARD }, /* Fast forward */
{ 0x07, 0x0c, KEY_REWIND }, /* Rewind */
{ 0x07, 0x0d, KEY_PLAY }, /* Play/Pause */
{ 0x07, 0x0e, KEY_VOLUMEUP }, /* Volume + */
{ 0x07, 0x0f, KEY_VOLUMEDOWN }, /* Volume - */
{ 0x07, 0x10, KEY_RECORD }, /* Record */
{ 0x07, 0x11, KEY_STOP }, /* Stop */
{ 0x07, 0x12, KEY_ZOOM }, /* TV */
{ 0x07, 0x13, KEY_EPG }, /* Info/EPG */
{ 0x07, 0x14, KEY_CHANNELDOWN }, /* Channel - */
{ 0x07, 0x15, KEY_CHANNELUP }, /* Channel + */
{ 0x07, 0x1e, KEY_1 },
{ 0x07, 0x1f, KEY_2 },
{ 0x07, 0x20, KEY_3 },
{ 0x07, 0x21, KEY_4 },
{ 0x07, 0x22, KEY_5 },
{ 0x07, 0x23, KEY_6 },
{ 0x07, 0x24, KEY_7 },
{ 0x07, 0x25, KEY_8 },
{ 0x07, 0x26, KEY_9 },
{ 0x07, 0x08, KEY_LEFT }, /* LEFT */
{ 0x07, 0x27, KEY_0 },
};
static u8 af9015_ir_table_trekstor[] = {
0x00, 0xff, 0x86, 0x79, 0x04, 0x07, 0x00,
0x00, 0xff, 0x85, 0x7a, 0x05, 0x07, 0x00,
0x00, 0xff, 0x87, 0x78, 0x06, 0x07, 0x00,
0x00, 0xff, 0x8c, 0x73, 0x07, 0x07, 0x00,
0x00, 0xff, 0x89, 0x76, 0x09, 0x07, 0x00,
0x00, 0xff, 0x88, 0x77, 0x0a, 0x07, 0x00,
0x00, 0xff, 0x8a, 0x75, 0x0b, 0x07, 0x00,
0x00, 0xff, 0x9e, 0x61, 0x0c, 0x07, 0x00,
0x00, 0xff, 0x8d, 0x72, 0x0d, 0x07, 0x00,
0x00, 0xff, 0x8b, 0x74, 0x0e, 0x07, 0x00,
0x00, 0xff, 0x9b, 0x64, 0x0f, 0x07, 0x00,
0x00, 0xff, 0x9d, 0x62, 0x10, 0x07, 0x00,
0x00, 0xff, 0x8e, 0x71, 0x11, 0x07, 0x00,
0x00, 0xff, 0x9c, 0x63, 0x12, 0x07, 0x00,
0x00, 0xff, 0x8f, 0x70, 0x13, 0x07, 0x00,
0x00, 0xff, 0x93, 0x6c, 0x14, 0x07, 0x00,
0x00, 0xff, 0x97, 0x68, 0x15, 0x07, 0x00,
0x00, 0xff, 0x92, 0x6d, 0x1e, 0x07, 0x00,
0x00, 0xff, 0x96, 0x69, 0x1f, 0x07, 0x00,
0x00, 0xff, 0x9a, 0x65, 0x20, 0x07, 0x00,
0x00, 0xff, 0x91, 0x6e, 0x21, 0x07, 0x00,
0x00, 0xff, 0x95, 0x6a, 0x22, 0x07, 0x00,
0x00, 0xff, 0x99, 0x66, 0x23, 0x07, 0x00,
0x00, 0xff, 0x90, 0x6f, 0x24, 0x07, 0x00,
0x00, 0xff, 0x94, 0x6b, 0x25, 0x07, 0x00,
0x00, 0xff, 0x98, 0x67, 0x26, 0x07, 0x00,
0x00, 0xff, 0x9f, 0x60, 0x08, 0x07, 0x00,
0x00, 0xff, 0x84, 0x7b, 0x27, 0x07, 0x00,
};
#endif

View File

@ -250,6 +250,7 @@ static int ce6230_probe(struct usb_interface *intf,
static struct usb_device_id ce6230_table[] = {
{ USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500) },
{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ce6230_table);
@ -284,13 +285,18 @@ static struct dvb_usb_device_properties ce6230_properties = {
.i2c_algo = &ce6230_i2c_algo,
.num_device_descs = 1,
.num_device_descs = 2,
.devices = {
{
.name = "Intel CE9500 reference design",
.cold_ids = {NULL},
.warm_ids = {&ce6230_table[0], NULL},
},
{
.name = "AVerMedia A310 USB 2.0 DVB-T tuner",
.cold_ids = {NULL},
.warm_ids = {&ce6230_table[1], NULL},
},
}
};

View File

@ -65,6 +65,7 @@
#define USB_PID_AFATECH_AF9005 0x9020
#define USB_PID_AFATECH_AF9015_9015 0x9015
#define USB_PID_AFATECH_AF9015_9016 0x9016
#define USB_PID_TREKSTOR_DVBT 0x901b
#define USB_VID_ALINK_DTU 0xf170
#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_ANYSEE 0x861f
@ -102,6 +103,7 @@
#define USB_PID_KWORLD_399U 0xe399
#define USB_PID_KWORLD_395U 0xe396
#define USB_PID_KWORLD_395U_2 0xe39b
#define USB_PID_KWORLD_395U_3 0xe395
#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
@ -167,6 +169,8 @@
#define USB_PID_AVERMEDIA_VOLAR_X 0xa815
#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150
#define USB_PID_AVERMEDIA_A309 0xa309
#define USB_PID_AVERMEDIA_A310 0xa310
#define USB_PID_AVERMEDIA_A850 0x850a
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081

View File

@ -151,7 +151,7 @@ static void debug_fcp(const u8 *data, int length)
subunit_type = data[1] >> 3;
subunit_id = data[1] & 7;
op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2];
printk(KERN_INFO "%ssu=%x.%x l=%zu: %-8s - %s\n",
printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n",
prefix, subunit_type, subunit_id, length,
debug_fcp_ctype(data[0]),
debug_fcp_opcode(op, data, length));

View File

@ -513,6 +513,13 @@ config DVB_LGS8GL5
help
A DMB-TH tuner module. Say Y when you want to support this frontend.
config DVB_LGS8GXX
tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator"
depends on DVB_CORE && I2C
default m if DVB_FE_CUSTOMISE
help
A DMB-TH tuner module. Say Y when you want to support this frontend.
comment "Tools to develop new frontends"
config DVB_DUMMY_FE

View File

@ -61,6 +61,7 @@ obj-$(CONFIG_DVB_TDA10048) += tda10048.o
obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o
obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
obj-$(CONFIG_DVB_AF9013) += af9013.o
obj-$(CONFIG_DVB_CX24116) += cx24116.o

View File

@ -652,7 +652,7 @@ static int au8522_reset(struct v4l2_subdev *sd, u32 val)
}
static int au8522_s_video_routing(struct v4l2_subdev *sd,
const struct v4l2_routing *route)
u32 input, u32 output, u32 config)
{
struct au8522_state *state = to_state(sd);
@ -663,11 +663,11 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd,
closed), and then came back to analog mode */
au8522_writereg(state, 0x106, 1);
if (route->input == AU8522_COMPOSITE_CH1) {
if (input == AU8522_COMPOSITE_CH1) {
au8522_setup_cvbs_mode(state);
} else if (route->input == AU8522_SVIDEO_CH13) {
} else if (input == AU8522_SVIDEO_CH13) {
au8522_setup_svideo_mode(state);
} else if (route->input == AU8522_COMPOSITE_CH4_SIF) {
} else if (input == AU8522_COMPOSITE_CH4_SIF) {
au8522_setup_cvbs_tuner_mode(state);
} else {
printk(KERN_ERR "au8522 mode not currently supported\n");
@ -677,10 +677,10 @@ static int au8522_s_video_routing(struct v4l2_subdev *sd,
}
static int au8522_s_audio_routing(struct v4l2_subdev *sd,
const struct v4l2_routing *route)
u32 input, u32 output, u32 config)
{
struct au8522_state *state = to_state(sd);
set_audio_input(state, route->input);
set_audio_input(state, input);
return 0;
}

View File

@ -0,0 +1,816 @@
/*
* Support for Legend Silicon DMB-TH demodulator
* LGS8913, LGS8GL5
* experimental support LGS8G42, LGS8G52
*
* Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com>
* Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
*
* 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 <asm/div64.h>
#include "dvb_frontend.h"
#include "lgs8gxx.h"
#include "lgs8gxx_priv.h"
#define dprintk(args...) \
do { \
if (debug) \
printk(KERN_DEBUG "lgs8gxx: " args); \
} while (0)
static int debug;
static int fake_signal_str;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
module_param(fake_signal_str, int, 0644);
MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
"Signal strength calculation is slow.(default:off).");
/* LGS8GXX internal helper functions */
static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
{
int ret;
u8 buf[] = { reg, data };
struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
msg.addr = priv->config->demod_address;
if (reg >= 0xC0)
msg.addr += 0x02;
if (debug >= 2)
printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
__func__, reg, data);
ret = i2c_transfer(priv->i2c, &msg, 1);
if (ret != 1)
dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n",
__func__, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
{
int ret;
u8 dev_addr;
u8 b0[] = { reg };
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
{ .flags = 0, .buf = b0, .len = 1 },
{ .flags = I2C_M_RD, .buf = b1, .len = 1 },
};
dev_addr = priv->config->demod_address;
if (reg >= 0xC0)
dev_addr += 0x02;
msg[1].addr = msg[0].addr = dev_addr;
ret = i2c_transfer(priv->i2c, msg, 2);
if (ret != 2) {
dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n",
__func__, reg, ret);
return -1;
}
*p_data = b1[0];
if (debug >= 2)
printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
__func__, reg, b1[0]);
return 0;
}
static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
{
lgs8gxx_write_reg(priv, 0x02, 0x00);
msleep(1);
lgs8gxx_write_reg(priv, 0x02, 0x01);
msleep(100);
return 0;
}
static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
{
const struct lgs8gxx_config *config = priv->config;
u8 if_conf;
if_conf = 0x10; /* AGC output on; */
if_conf |=
((config->ext_adc) ? 0x80 : 0x00) |
((config->if_neg_center) ? 0x04 : 0x00) |
((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
((config->ext_adc && config->adc_signed) ? 0x02 : 0x00) |
((config->ext_adc && config->if_neg_edge) ? 0x01 : 0x00);
if (config->ext_adc &&
(config->prod == LGS8GXX_PROD_LGS8G52)) {
lgs8gxx_write_reg(priv, 0xBA, 0x40);
}
lgs8gxx_write_reg(priv, 0x07, if_conf);
return 0;
}
static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
{
u64 val;
u32 v32;
u32 if_clk;
if_clk = priv->config->if_clk_freq;
val = freq;
if (freq != 0) {
val *= (u64)1 << 32;
if (if_clk != 0)
do_div(val, if_clk);
v32 = val & 0xFFFFFFFF;
dprintk("Set IF Freq to %dkHz\n", freq);
} else {
v32 = 0;
dprintk("Set IF Freq to baseband\n");
}
dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
return 0;
}
static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
{
u8 t;
if (priv->config->prod == LGS8GXX_PROD_LGS8913)
lgs8gxx_write_reg(priv, 0xC6, 0x01);
lgs8gxx_read_reg(priv, 0x7E, &t);
lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
/* clear FEC self reset */
lgs8gxx_read_reg(priv, 0xC5, &t);
lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
/* FEC auto detect */
lgs8gxx_write_reg(priv, 0xC1, 0x03);
lgs8gxx_read_reg(priv, 0x7C, &t);
t = (t & 0x8C) | 0x03;
lgs8gxx_write_reg(priv, 0x7C, t);
}
if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
/* BER test mode */
lgs8gxx_read_reg(priv, 0xC3, &t);
t = (t & 0xEF) | 0x10;
lgs8gxx_write_reg(priv, 0xC3, t);
}
if (priv->config->prod == LGS8GXX_PROD_LGS8G52)
lgs8gxx_write_reg(priv, 0xD9, 0x40);
return 0;
}
static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
{
int ret = 0;
u8 t;
/* turn off auto-detect; manual settings */
lgs8gxx_write_reg(priv, 0x7E, 0);
if (priv->config->prod == LGS8GXX_PROD_LGS8913)
lgs8gxx_write_reg(priv, 0xC1, 0);
ret = lgs8gxx_read_reg(priv, 0xC5, &t);
t = (t & 0xE0) | 0x06;
lgs8gxx_write_reg(priv, 0xC5, t);
lgs8gxx_soft_reset(priv);
return 0;
}
static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
{
int ret = 0;
u8 t;
ret = lgs8gxx_read_reg(priv, 0x4B, &t);
if (ret != 0)
return ret;
*locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
return 0;
}
static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
u8 *finished)
{
int ret = 0;
u8 t;
ret = lgs8gxx_read_reg(priv, 0xA4, &t);
if (ret != 0)
return ret;
*finished = ((t & 0x3) == 0x1) ? 1 : 0;
return 0;
}
static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked)
{
int err;
u8 ad_fini = 0;
if (gi == GI_945)
dprintk("try GI 945\n");
else if (gi == GI_595)
dprintk("try GI 595\n");
else if (gi == GI_420)
dprintk("try GI 420\n");
lgs8gxx_write_reg(priv, 0x04, gi);
lgs8gxx_soft_reset(priv);
msleep(50);
err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
if (err != 0)
return err;
if (ad_fini) {
err = lgs8gxx_is_locked(priv, locked);
if (err != 0)
return err;
}
return 0;
}
static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
u8 *detected_param, u8 *gi)
{
int i, j;
int err = 0;
u8 locked = 0, tmp_gi;
dprintk("%s\n", __func__);
lgs8gxx_set_mode_auto(priv);
/* Guard Interval */
lgs8gxx_write_reg(priv, 0x03, 00);
for (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) {
tmp_gi = GI_945;
err = lgs8gxx_autolock_gi(priv, GI_945, &locked);
if (err)
goto out;
if (locked)
goto locked;
}
for (j = 0; j < 2; j++) {
tmp_gi = GI_420;
err = lgs8gxx_autolock_gi(priv, GI_420, &locked);
if (err)
goto out;
if (locked)
goto locked;
}
tmp_gi = GI_595;
err = lgs8gxx_autolock_gi(priv, GI_595, &locked);
if (err)
goto out;
if (locked)
goto locked;
}
locked:
if ((err == 0) && (locked == 1)) {
u8 t;
lgs8gxx_read_reg(priv, 0xA2, &t);
*detected_param = t;
if (tmp_gi == GI_945)
dprintk("GI 945 locked\n");
else if (tmp_gi == GI_595)
dprintk("GI 595 locked\n");
else if (tmp_gi == GI_420)
dprintk("GI 420 locked\n");
*gi = tmp_gi;
}
if (!locked)
err = -1;
out:
return err;
}
static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
{
s8 err;
u8 gi = 0x2;
u8 detected_param = 0;
err = lgs8gxx_auto_detect(priv, &detected_param, &gi);
if (err != 0) {
dprintk("lgs8gxx_auto_detect failed\n");
}
/* Apply detected parameters */
if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
u8 inter_leave_len = detected_param & TIM_MASK ;
inter_leave_len = (inter_leave_len == TIM_LONG) ? 0x60 : 0x40;
detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK;
detected_param |= inter_leave_len;
}
lgs8gxx_write_reg(priv, 0x7D, detected_param);
if (priv->config->prod == LGS8GXX_PROD_LGS8913)
lgs8gxx_write_reg(priv, 0xC0, detected_param);
/* lgs8gxx_soft_reset(priv); */
/* Enter manual mode */
lgs8gxx_set_mode_manual(priv);
switch (gi) {
case GI_945:
priv->curr_gi = 945; break;
case GI_595:
priv->curr_gi = 595; break;
case GI_420:
priv->curr_gi = 420; break;
default:
priv->curr_gi = 945; break;
}
}
static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
u8 serial, u8 clk_pol, u8 clk_gated)
{
int ret = 0;
u8 t;
ret = lgs8gxx_read_reg(priv, 0xC2, &t);
if (ret != 0)
return ret;
t &= 0xF8;
t |= serial ? TS_SERIAL : TS_PARALLEL;
t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
ret = lgs8gxx_write_reg(priv, 0xC2, t);
if (ret != 0)
return ret;
return 0;
}
/* LGS8913 demod frontend functions */
static int lgs8913_init(struct lgs8gxx_state *priv)
{
u8 t;
/* LGS8913 specific */
lgs8gxx_write_reg(priv, 0xc1, 0x3);
lgs8gxx_read_reg(priv, 0x7c, &t);
lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3);
/* LGS8913 specific */
lgs8gxx_read_reg(priv, 0xc3, &t);
lgs8gxx_write_reg(priv, 0xc3, t&0x10);
return 0;
}
static int lgs8gxx_init(struct dvb_frontend *fe)
{
struct lgs8gxx_state *priv =
(struct lgs8gxx_state *)fe->demodulator_priv;
const struct lgs8gxx_config *config = priv->config;
u8 data = 0;
s8 err;
dprintk("%s\n", __func__);
lgs8gxx_read_reg(priv, 0, &data);
dprintk("reg 0 = 0x%02X\n", data);
/* Setup MPEG output format */
err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
config->ts_clk_pol,
config->ts_clk_gated);
if (err != 0)
return -EIO;
if (config->prod == LGS8GXX_PROD_LGS8913)
lgs8913_init(priv);
lgs8gxx_set_if_freq(priv, priv->config->if_freq);
if (config->prod != LGS8GXX_PROD_LGS8913)
lgs8gxx_set_ad_mode(priv);
return 0;
}
static void lgs8gxx_release(struct dvb_frontend *fe)
{
struct lgs8gxx_state *state = fe->demodulator_priv;
dprintk("%s\n", __func__);
kfree(state);
}
static int lgs8gxx_write(struct dvb_frontend *fe, u8 *buf, int len)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
if (len != 2)
return -EINVAL;
return lgs8gxx_write_reg(priv, buf[0], buf[1]);
}
static int lgs8gxx_set_fe(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fe_params)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
dprintk("%s\n", __func__);
/* set frequency */
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe, fe_params);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
/* start auto lock */
lgs8gxx_auto_lock(priv);
msleep(10);
return 0;
}
static int lgs8gxx_get_fe(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fe_params)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
u8 t;
dprintk("%s\n", __func__);
/* TODO: get real readings from device */
/* inversion status */
fe_params->inversion = INVERSION_OFF;
/* bandwidth */
fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
lgs8gxx_read_reg(priv, 0x7D, &t);
fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
fe_params->u.ofdm.code_rate_LP = FEC_AUTO;
/* constellation */
switch (t & SC_MASK) {
case SC_QAM64:
fe_params->u.ofdm.constellation = QAM_64;
break;
case SC_QAM32:
fe_params->u.ofdm.constellation = QAM_32;
break;
case SC_QAM16:
fe_params->u.ofdm.constellation = QAM_16;
break;
case SC_QAM4:
case SC_QAM4NR:
fe_params->u.ofdm.constellation = QPSK;
break;
default:
fe_params->u.ofdm.constellation = QAM_64;
}
/* transmission mode */
fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
/* guard interval */
fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
/* hierarchy */
fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
return 0;
}
static
int lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *fesettings)
{
/* FIXME: copy from tda1004x.c */
fesettings->min_delay_ms = 800;
fesettings->step_size = 0;
fesettings->max_drift = 0;
return 0;
}
static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
s8 ret;
u8 t;
dprintk("%s\n", __func__);
ret = lgs8gxx_read_reg(priv, 0x4B, &t);
if (ret != 0)
return -EIO;
dprintk("Reg 0x4B: 0x%02X\n", t);
*fe_status = 0;
if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
if ((t & 0x40) == 0x40)
*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
if ((t & 0x80) == 0x80)
*fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_LOCK;
} else {
if ((t & 0x80) == 0x80)
*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
}
/* success */
dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
return 0;
}
static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal)
{
u16 v;
u8 agc_lvl[2], cat;
dprintk("%s()\n", __func__);
lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]);
lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]);
v = agc_lvl[0];
v <<= 8;
v |= agc_lvl[1];
dprintk("agc_lvl: 0x%04X\n", v);
if (v < 0x100)
cat = 0;
else if (v < 0x190)
cat = 5;
else if (v < 0x2A8)
cat = 4;
else if (v < 0x381)
cat = 3;
else if (v < 0x400)
cat = 2;
else if (v == 0x400)
cat = 1;
else
cat = 0;
*signal = cat;
return 0;
}
static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
{
u8 t; s8 ret;
s16 max_strength = 0;
u8 str;
u16 i, gi = priv->curr_gi;
dprintk("%s\n", __func__);
ret = lgs8gxx_read_reg(priv, 0x4B, &t);
if (ret != 0)
return -EIO;
if (fake_signal_str) {
if ((t & 0xC0) == 0xC0) {
dprintk("Fake signal strength as 50\n");
*signal = 0x32;
} else
*signal = 0;
return 0;
}
dprintk("gi = %d\n", gi);
for (i = 0; i < gi; i++) {
if ((i & 0xFF) == 0)
lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8));
lgs8gxx_write_reg(priv, 0x83, i & 0xFF);
lgs8gxx_read_reg(priv, 0x94, &str);
if (max_strength < str)
max_strength = str;
}
*signal = max_strength;
dprintk("%s: signal=0x%02X\n", __func__, *signal);
lgs8gxx_read_reg(priv, 0x95, &t);
dprintk("%s: AVG Noise=0x%02X\n", __func__, t);
return 0;
}
static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
if (priv->config->prod == LGS8GXX_PROD_LGS8913)
return lgs8913_read_signal_strength(priv, signal);
else
return lgs8gxx_read_signal_agc(priv, signal);
}
static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
u8 t;
*snr = 0;
lgs8gxx_read_reg(priv, 0x95, &t);
dprintk("AVG Noise=0x%02X\n", t);
*snr = 256 - t;
*snr <<= 8;
dprintk("snr=0x%x\n", *snr);
return 0;
}
static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
*ucblocks = 0;
dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
return 0;
}
static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
u8 r0, r1, r2, r3;
u32 total_cnt, err_cnt;
dprintk("%s\n", __func__);
lgs8gxx_write_reg(priv, 0xc6, 0x01);
lgs8gxx_write_reg(priv, 0xc6, 0x41);
lgs8gxx_write_reg(priv, 0xc6, 0x01);
msleep(200);
lgs8gxx_write_reg(priv, 0xc6, 0x81);
lgs8gxx_read_reg(priv, 0xd0, &r0);
lgs8gxx_read_reg(priv, 0xd1, &r1);
lgs8gxx_read_reg(priv, 0xd2, &r2);
lgs8gxx_read_reg(priv, 0xd3, &r3);
total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0);
lgs8gxx_read_reg(priv, 0xd4, &r0);
lgs8gxx_read_reg(priv, 0xd5, &r1);
lgs8gxx_read_reg(priv, 0xd6, &r2);
lgs8gxx_read_reg(priv, 0xd7, &r3);
err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0);
dprintk("error=%d total=%d\n", err_cnt, total_cnt);
if (total_cnt == 0)
*ber = 0;
else
*ber = err_cnt * 100 / total_cnt;
dprintk("%s: ber=0x%x\n", __func__, *ber);
return 0;
}
static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct lgs8gxx_state *priv = fe->demodulator_priv;
if (priv->config->tuner_address == 0)
return 0;
if (enable) {
u8 v = 0x80 | priv->config->tuner_address;
return lgs8gxx_write_reg(priv, 0x01, v);
}
return lgs8gxx_write_reg(priv, 0x01, 0);
}
static struct dvb_frontend_ops lgs8gxx_ops = {
.info = {
.name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
.type = FE_OFDM,
.frequency_min = 474000000,
.frequency_max = 858000000,
.frequency_stepsize = 10000,
.caps =
FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO
},
.release = lgs8gxx_release,
.init = lgs8gxx_init,
.write = lgs8gxx_write,
.i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
.set_frontend = lgs8gxx_set_fe,
.get_frontend = lgs8gxx_get_fe,
.get_tune_settings = lgs8gxx_get_tune_settings,
.read_status = lgs8gxx_read_status,
.read_ber = lgs8gxx_read_ber,
.read_signal_strength = lgs8gxx_read_signal_strength,
.read_snr = lgs8gxx_read_snr,
.read_ucblocks = lgs8gxx_read_ucblocks,
};
struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
struct i2c_adapter *i2c)
{
struct lgs8gxx_state *priv = NULL;
u8 data = 0;
dprintk("%s()\n", __func__);
if (config == NULL || i2c == NULL)
return NULL;
priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL);
if (priv == NULL)
goto error_out;
priv->config = config;
priv->i2c = i2c;
/* check if the demod is there */
if (lgs8gxx_read_reg(priv, 0, &data) != 0) {
dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n",
__func__, priv->config->demod_address);
goto error_out;
}
lgs8gxx_read_reg(priv, 1, &data);
memcpy(&priv->frontend.ops, &lgs8gxx_ops,
sizeof(struct dvb_frontend_ops));
priv->frontend.demodulator_priv = priv;
return &priv->frontend;
error_out:
dprintk("%s() error_out\n", __func__);
kfree(priv);
return NULL;
}
EXPORT_SYMBOL(lgs8gxx_attach);
MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,90 @@
/*
* Support for Legend Silicon DMB-TH demodulator
* LGS8913, LGS8GL5
* experimental support LGS8G42, LGS8G52
*
* Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com>
* Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
*
* 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.
*
*/
#ifndef __LGS8GXX_H__
#define __LGS8GXX_H__
#include <linux/dvb/frontend.h>
#include <linux/i2c.h>
#define LGS8GXX_PROD_LGS8913 0
#define LGS8GXX_PROD_LGS8GL5 1
#define LGS8GXX_PROD_LGS8G42 3
#define LGS8GXX_PROD_LGS8G52 4
#define LGS8GXX_PROD_LGS8G54 5
struct lgs8gxx_config {
/* product type */
u8 prod;
/* the demodulator's i2c address */
u8 demod_address;
/* parallel or serial transport stream */
u8 serial_ts;
/* transport stream polarity*/
u8 ts_clk_pol;
/* transport stream clock gated by ts_valid */
u8 ts_clk_gated;
/* A/D Clock frequency */
u32 if_clk_freq; /* in kHz */
/* IF frequency */
u32 if_freq; /* in kHz */
/*Use External ADC*/
u8 ext_adc;
/*External ADC output two's complement*/
u8 adc_signed;
/*Sample IF data at falling edge of IF_CLK*/
u8 if_neg_edge;
/*IF use Negative center frequency*/
u8 if_neg_center;
/* slave address and configuration of the tuner */
u8 tuner_address;
};
#if defined(CONFIG_DVB_LGS8GXX) || \
(defined(CONFIG_DVB_LGS8GXX_MODULE) && defined(MODULE))
extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
struct i2c_adapter *i2c);
#else
static inline
struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
struct i2c_adapter *i2c) {
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
#endif /* CONFIG_DVB_LGS8GXX */
#endif /* __LGS8GXX_H__ */

View File

@ -0,0 +1,70 @@
/*
* Support for Legend Silicon DMB-TH demodulator
* LGS8913, LGS8GL5
* experimental support LGS8G42, LGS8G52
*
* Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com>
* Copyright (C) 2008 Sirius International (Hong Kong) Limited
* Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
*
* 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.
*
*/
#ifndef LGS8913_PRIV_H
#define LGS8913_PRIV_H
struct lgs8gxx_state {
struct i2c_adapter *i2c;
/* configuration settings */
const struct lgs8gxx_config *config;
struct dvb_frontend frontend;
u16 curr_gi; /* current guard interval */
};
#define SC_MASK 0x1C /* Sub-Carrier Modulation Mask */
#define SC_QAM64 0x10 /* 64QAM modulation */
#define SC_QAM32 0x0C /* 32QAM modulation */
#define SC_QAM16 0x08 /* 16QAM modulation */
#define SC_QAM4NR 0x04 /* 4QAM modulation */
#define SC_QAM4 0x00 /* 4QAM modulation */
#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */
#define LGS_FEC_0_4 0x00 /* FEC Rate 0.4 */
#define LGS_FEC_0_6 0x01 /* FEC Rate 0.6 */
#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */
#define TIM_MASK 0x20 /* Time Interleave Length Mask */
#define TIM_LONG 0x00 /* Time Interleave Length = 720 */
#define TIM_MIDDLE 0x20 /* Time Interleave Length = 240 */
#define CF_MASK 0x80 /* Control Frame Mask */
#define CF_EN 0x80 /* Control Frame On */
#define GI_MASK 0x03 /* Guard Interval Mask */
#define GI_420 0x00 /* 1/9 Guard Interval */
#define GI_595 0x01 /* */
#define GI_945 0x02 /* 1/4 Guard Interval */
#define TS_PARALLEL 0x00 /* Parallel TS Output a.k.a. SPI */
#define TS_SERIAL 0x01 /* Serial TS Output a.k.a. SSI */
#define TS_CLK_NORMAL 0x00 /* MPEG Clock Normal */
#define TS_CLK_INVERTED 0x02 /* MPEG Clock Inverted */
#define TS_CLK_GATED 0x00 /* MPEG clock gated */
#define TS_CLK_FREERUN 0x04 /* MPEG clock free running*/
#endif

View File

@ -33,6 +33,9 @@
History:
Version 0.45:
Converted to v4l2_device.
Version 0.44:
Add suspend/resume functions, fix unplug of device,
a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com>
@ -88,7 +91,7 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <linux/usb.h>
@ -97,39 +100,8 @@
*/
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
#define DRIVER_VERSION "v0.44"
#define RADIO_VERSION KERNEL_VERSION(0, 4, 4)
static struct v4l2_queryctrl radio_qctrl[] = {
{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.default_value = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},
/* HINT: the disabled controls are only here to satify kradio and such apps */
{ .id = V4L2_CID_AUDIO_VOLUME,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_BALANCE,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_BASS,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_TREBLE,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_LOUDNESS,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
};
#define DRIVER_VERSION "v0.45"
#define RADIO_VERSION KERNEL_VERSION(0, 4, 5)
#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
@ -167,6 +139,8 @@ module_param(radio_nr, int, 0);
struct dsbr100_device {
struct usb_device *usbdev;
struct video_device videodev;
struct v4l2_device v4l2_dev;
u8 *transfer_buffer;
struct mutex lock; /* buffer locking */
int curfreq;
@ -384,6 +358,7 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
mutex_unlock(&radio->lock);
video_unregister_device(&radio->videodev);
v4l2_device_disconnect(&radio->v4l2_dev);
}
@ -479,14 +454,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
int i;
for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
if (qc->id && qc->id == radio_qctrl[i].id) {
memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
return 0;
}
switch (qc->id) {
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
}
return -EINVAL;
}
@ -656,6 +628,7 @@ static void usb_dsbr100_video_device_release(struct video_device *videodev)
{
struct dsbr100_device *radio = videodev_to_radio(videodev);
v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio->transfer_buffer);
kfree(radio);
}
@ -683,22 +656,15 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
};
/* V4L2 interface */
static struct video_device dsbr100_videodev_data = {
.name = "D-Link DSB-R 100",
.fops = &usb_dsbr100_fops,
.ioctl_ops = &usb_dsbr100_ioctl_ops,
.release = usb_dsbr100_video_device_release,
};
/* check if the device is present and register with v4l and usb if it is */
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dsbr100_device *radio;
struct v4l2_device *v4l2_dev;
int retval;
radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
radio = kzalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
if (!radio)
return -ENOMEM;
@ -710,17 +676,35 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
return -ENOMEM;
}
v4l2_dev = &radio->v4l2_dev;
retval = v4l2_device_register(&intf->dev, v4l2_dev);
if (retval < 0) {
v4l2_err(v4l2_dev, "couldn't register v4l2_device\n");
kfree(radio->transfer_buffer);
kfree(radio);
return retval;
}
strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name));
radio->videodev.v4l2_dev = v4l2_dev;
radio->videodev.fops = &usb_dsbr100_fops;
radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops;
radio->videodev.release = usb_dsbr100_video_device_release;
mutex_init(&radio->lock);
radio->videodev = dsbr100_videodev_data;
radio->removed = 0;
radio->users = 0;
radio->usbdev = interface_to_usbdev(intf);
radio->curfreq = FREQ_MIN * FREQ_MUL;
video_set_drvdata(&radio->videodev, radio);
retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
if (retval < 0) {
dev_err(&intf->dev, "couldn't register video device\n");
v4l2_err(v4l2_dev, "couldn't register video device\n");
v4l2_device_unregister(v4l2_dev);
kfree(radio->transfer_buffer);
kfree(radio);
return -EIO;

View File

@ -355,20 +355,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int rtrack_open(struct file *file)
{
return 0;
}
static int rtrack_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations rtrack_fops = {
.owner = THIS_MODULE,
.open = rtrack_open,
.release = rtrack_release,
.ioctl = video_ioctl2,
};

View File

@ -318,20 +318,8 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
return -EINVAL;
}
static int aztech_open(struct file *file)
{
return 0;
}
static int aztech_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations aztech_fops = {
.owner = THIS_MODULE,
.open = aztech_open,
.release = aztech_release,
.ioctl = video_ioctl2,
};

View File

@ -356,20 +356,8 @@ static struct pci_device_id gemtek_pci_id[] =
MODULE_DEVICE_TABLE(pci, gemtek_pci_id);
static int gemtek_pci_open(struct file *file)
{
return 0;
}
static int gemtek_pci_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations gemtek_pci_fops = {
.owner = THIS_MODULE,
.open = gemtek_pci_open,
.release = gemtek_pci_release,
.ioctl = video_ioctl2,
};

View File

@ -375,20 +375,9 @@ static int gemtek_probe(struct gemtek *gt)
/*
* Video 4 Linux stuff.
*/
static int gemtek_open(struct file *file)
{
return 0;
}
static int gemtek_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations gemtek_fops = {
.owner = THIS_MODULE,
.open = gemtek_open,
.release = gemtek_release,
.ioctl = video_ioctl2,
};

View File

@ -292,20 +292,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int maestro_open(struct file *file)
{
return 0;
}
static int maestro_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations maestro_fops = {
.owner = THIS_MODULE,
.open = maestro_open,
.release = maestro_release,
.ioctl = video_ioctl2,
};

View File

@ -339,20 +339,8 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
return -EINVAL;
}
static int maxiradio_open(struct file *file)
{
return 0;
}
static int maxiradio_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations maxiradio_fops = {
.owner = THIS_MODULE,
.open = maxiradio_open,
.release = maxiradio_release,
.ioctl = video_ioctl2,
};

View File

@ -43,6 +43,7 @@
* Douglas Schilling Landgraf <dougsland@gmail.com> and
* David Ellingsworth <david@identd.dyndns.org>
* for discussion, help and support.
* Version 0.11: Converted to v4l2_device.
*
* Many things to do:
* - Correct power managment of device (suspend & resume)
@ -59,7 +60,7 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <linux/usb.h>
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
@ -67,8 +68,8 @@
/* driver and module definitions */
#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
#define DRIVER_VERSION "0.10"
#define RADIO_VERSION KERNEL_VERSION(0, 1, 0)
#define DRIVER_VERSION "0.11"
#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
@ -113,38 +114,6 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(radio_nr, "Radio Nr");
static struct v4l2_queryctrl radio_qctrl[] = {
{
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},
/* HINT: the disabled controls are only here to satify kradio and such apps */
{ .id = V4L2_CID_AUDIO_VOLUME,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_BALANCE,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_BASS,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_TREBLE,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
{
.id = V4L2_CID_AUDIO_LOUDNESS,
.flags = V4L2_CTRL_FLAG_DISABLED,
},
};
static int usb_amradio_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void usb_amradio_disconnect(struct usb_interface *intf);
@ -159,6 +128,7 @@ struct amradio_device {
/* reference to USB and video device */
struct usb_device *usbdev;
struct video_device *videodev;
struct v4l2_device v4l2_dev;
unsigned char *buffer;
struct mutex lock; /* buffer locking */
@ -329,6 +299,7 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
video_unregister_device(radio->videodev);
v4l2_device_disconnect(&radio->v4l2_dev);
}
/* vidioc_querycap - query device capabilities */
@ -463,14 +434,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
int i;
for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
if (qc->id && qc->id == radio_qctrl[i].id) {
memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
return 0;
}
switch (qc->id) {
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
}
return -EINVAL;
}
@ -671,34 +639,29 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
};
static void usb_amradio_device_release(struct video_device *videodev)
static void usb_amradio_video_device_release(struct video_device *videodev)
{
struct amradio_device *radio = video_get_drvdata(videodev);
/* we call v4l to free radio->videodev */
video_device_release(videodev);
v4l2_device_unregister(&radio->v4l2_dev);
/* free rest memory */
kfree(radio->buffer);
kfree(radio);
}
/* V4L2 interface */
static struct video_device amradio_videodev_template = {
.name = "AverMedia MR 800 USB FM Radio",
.fops = &usb_amradio_fops,
.ioctl_ops = &usb_amradio_ioctl_ops,
.release = usb_amradio_device_release,
};
/* check if the device is present and register with v4l and usb if it is */
static int usb_amradio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct amradio_device *radio;
struct v4l2_device *v4l2_dev;
int retval;
radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL);
radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL);
if (!radio) {
dev_err(&intf->dev, "kmalloc for amradio_device failed\n");
@ -713,6 +676,15 @@ static int usb_amradio_probe(struct usb_interface *intf,
return -ENOMEM;
}
v4l2_dev = &radio->v4l2_dev;
retval = v4l2_device_register(&intf->dev, v4l2_dev);
if (retval < 0) {
dev_err(&intf->dev, "couldn't register v4l2_device\n");
kfree(radio->buffer);
kfree(radio);
return retval;
}
radio->videodev = video_device_alloc();
if (!radio->videodev) {
@ -722,8 +694,11 @@ static int usb_amradio_probe(struct usb_interface *intf,
return -ENOMEM;
}
memcpy(radio->videodev, &amradio_videodev_template,
sizeof(amradio_videodev_template));
strlcpy(radio->videodev->name, v4l2_dev->name, sizeof(radio->videodev->name));
radio->videodev->v4l2_dev = v4l2_dev;
radio->videodev->fops = &usb_amradio_fops;
radio->videodev->ioctl_ops = &usb_amradio_ioctl_ops;
radio->videodev->release = usb_amradio_video_device_release;
radio->removed = 0;
radio->users = 0;
@ -734,10 +709,12 @@ static int usb_amradio_probe(struct usb_interface *intf,
mutex_init(&radio->lock);
video_set_drvdata(radio->videodev, radio);
retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
if (retval < 0) {
dev_err(&intf->dev, "could not register video device\n");
video_device_release(radio->videodev);
v4l2_device_unregister(v4l2_dev);
kfree(radio->buffer);
kfree(radio);
return -EIO;

View File

@ -260,20 +260,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int rtrack2_open(struct file *file)
{
return 0;
}
static int rtrack2_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations rtrack2_fops = {
.owner = THIS_MODULE,
.open = rtrack2_open,
.release = rtrack2_release,
.ioctl = video_ioctl2,
};

View File

@ -260,20 +260,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int fmi_open(struct file *file)
{
return 0;
}
static int fmi_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations fmi_fops = {
.owner = THIS_MODULE,
.open = fmi_open,
.release = fmi_release,
.ioctl = video_ioctl2,
};

View File

@ -377,20 +377,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int fmr2_open(struct file *file)
{
return 0;
}
static int fmr2_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations fmr2_fops = {
.owner = THIS_MODULE,
.open = fmr2_open,
.release = fmr2_release,
.ioctl = video_ioctl2,
};

View File

@ -1686,7 +1686,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
/* show some infos about the specific si470x device */
if (si470x_get_all_registers(radio) < 0) {
retval = -EIO;
goto err_all;
goto err_video;
}
printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[CHIPID]);
@ -1694,7 +1694,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
/* get software and hardware versions */
if (si470x_get_scratch_page_versions(radio) < 0) {
retval = -EIO;
goto err_all;
goto err_video;
}
printk(KERN_INFO DRIVER_NAME
": software version %d, hardware version %d\n",
@ -1727,7 +1727,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
goto err_all;
goto err_video;
}
/* rds buffer configuration */
@ -1749,8 +1749,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
return 0;
err_all:
video_device_release(radio->videodev);
kfree(radio->buffer);
err_video:
video_device_release(radio->videodev);
err_radio:
kfree(radio);
err_initial:

View File

@ -332,20 +332,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int terratec_open(struct file *file)
{
return 0;
}
static int terratec_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations terratec_fops = {
.owner = THIS_MODULE,
.open = terratec_open,
.release = terratec_release,
.ioctl = video_ioctl2,
};

View File

@ -338,20 +338,8 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int trust_open(struct file *file)
{
return 0;
}
static int trust_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations trust_fops = {
.owner = THIS_MODULE,
.open = trust_open,
.release = trust_release,
.ioctl = video_ioctl2,
};

View File

@ -314,20 +314,8 @@ static int vidioc_log_status(struct file *file, void *priv)
return 0;
}
static int typhoon_open(struct file *file)
{
return 0;
}
static int typhoon_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations typhoon_fops = {
.owner = THIS_MODULE,
.open = typhoon_open,
.release = typhoon_release,
.ioctl = video_ioctl2,
};

View File

@ -370,21 +370,9 @@ static int vidioc_s_audio(struct file *file, void *priv,
return a->index ? -EINVAL : 0;
}
static int zoltrix_open(struct file *file)
{
return 0;
}
static int zoltrix_release(struct file *file)
{
return 0;
}
static const struct v4l2_file_operations zoltrix_fops =
{
.owner = THIS_MODULE,
.open = zoltrix_open,
.release = zoltrix_release,
.ioctl = video_ioctl2,
};

View File

@ -746,6 +746,18 @@ config SOC_CAMERA_OV772X
help
This is a ov772x camera driver
config MX1_VIDEO
bool
config VIDEO_MX1
tristate "i.MX1/i.MXL CMOS Sensor Interface driver"
depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA
select FIQ
select VIDEOBUF_DMA_CONTIG
select MX1_VIDEO
---help---
This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
@ -795,6 +807,8 @@ source "drivers/media/video/hdpvr/Kconfig"
source "drivers/media/video/em28xx/Kconfig"
source "drivers/media/video/cx231xx/Kconfig"
source "drivers/media/video/usbvision/Kconfig"
source "drivers/media/video/usbvideo/Kconfig"
@ -904,5 +918,4 @@ config USB_S2255
This driver can be compiled as a module, called s2255drv.
endif # V4L_USB_DRIVERS
endif # VIDEO_CAPTURE_DRIVERS

View File

@ -10,7 +10,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
ifeq ($(CONFIG_COMPAT),y)
@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
@ -133,6 +134,7 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o
obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o

View File

@ -219,18 +219,19 @@ static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
return 0;
}
static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int adv7170_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7170 *encoder = to_adv7170(sd);
/* RJ: route->input = 0: input is from decoder
route->input = 1: input is from ZR36060
route->input = 2: color bar */
/* RJ: input = 0: input is from decoder
input = 1: input is from ZR36060
input = 2: color bar */
v4l2_dbg(1, debug, sd, "set input from %s\n",
route->input == 0 ? "decoder" : "ZR36060");
input == 0 ? "decoder" : "ZR36060");
switch (route->input) {
switch (input) {
case 0:
adv7170_write(sd, 0x01, 0x20);
adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */
@ -250,11 +251,11 @@ static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *
break;
default:
v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input);
v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
return -EINVAL;
}
v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]);
encoder->input = route->input;
v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
encoder->input = input;
return 0;
}

View File

@ -237,15 +237,16 @@ static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
return 0;
}
static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int adv7175_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7175 *encoder = to_adv7175(sd);
/* RJ: route->input = 0: input is from decoder
route->input = 1: input is from ZR36060
route->input = 2: color bar */
/* RJ: input = 0: input is from decoder
input = 1: input is from ZR36060
input = 2: color bar */
switch (route->input) {
switch (input) {
case 0:
adv7175_write(sd, 0x01, 0x00);
@ -288,11 +289,11 @@ static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *
break;
default:
v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input);
v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
return -EINVAL;
}
v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]);
encoder->input = route->input;
v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
encoder->input = input;
return 0;
}

View File

@ -4,6 +4,7 @@ config VIDEO_AU0828
depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2
select I2C_ALGOBIT
select VIDEO_TVEEPROM
select VIDEOBUF_VMALLOC
select DVB_AU8522 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE

View File

@ -46,6 +46,7 @@ struct au0828_board au0828_boards[] = {
.name = "Hauppauge HVR850",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
@ -70,6 +71,13 @@ struct au0828_board au0828_boards[] = {
.name = "Hauppauge HVR950Q",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
/* The au0828 hardware i2c implementation does not properly
support the xc5000's i2c clock stretching. So we need to
lower the clock frequency enough where the 15us clock
stretch fits inside of a normal clock cycle, or else the
au0828 fails to set the STOP bit. A 30 KHz clock puts the
clock pulse width at 18us */
.i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
@ -94,16 +102,19 @@ struct au0828_board au0828_boards[] = {
.name = "Hauppauge HVR950Q rev xxF8",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_DVICO_FUSIONHDTV7] = {
.name = "DViCO FusionHDTV USB",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
.name = "Hauppauge Woodbury",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
};
@ -200,8 +211,8 @@ void au0828_card_setup(struct au0828_dev *dev)
/* Load the analog demodulator driver (note this would need to
be abstracted out if we ever need to support a different
demod) */
sd = v4l2_i2c_new_subdev(&dev->i2c_adap, "au8522", "au8522",
0x8e >> 1);
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"au8522", "au8522", 0x8e >> 1);
if (sd == NULL)
printk(KERN_ERR "analog subdev registration failed\n");
}
@ -209,8 +220,8 @@ void au0828_card_setup(struct au0828_dev *dev)
/* Setup tuners */
if (dev->board.tuner_type != TUNER_ABSENT) {
/* Load the tuner module, which does the attach */
sd = v4l2_i2c_new_subdev(&dev->i2c_adap, "tuner", "tuner",
dev->board.tuner_addr);
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", "tuner", dev->board.tuner_addr);
if (sd == NULL)
printk(KERN_ERR "tuner subdev registration fail\n");

View File

@ -36,8 +36,6 @@ int au0828_debug;
module_param_named(debug, au0828_debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
static atomic_t au0828_instance = ATOMIC_INIT(0);
#define _AU0828_BULKPIPE 0x03
#define _BULKPIPESIZE 0xffff
@ -169,7 +167,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
static int au0828_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
int ifnum, retval, i;
int ifnum, retval;
struct au0828_dev *dev;
struct usb_device *usbdev = interface_to_usbdev(interface);
@ -197,10 +195,7 @@ static int au0828_usb_probe(struct usb_interface *interface,
usb_set_intfdata(interface, dev);
/* Create the v4l2_device */
i = atomic_inc_return(&au0828_instance) - 1;
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s-%03d",
"au0828", i);
retval = v4l2_device_register(&dev->usbdev->dev, &dev->v4l2_dev);
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
printk(KERN_ERR "%s() v4l2_device_register failed\n",
__func__);

View File

@ -39,13 +39,15 @@ MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, REG_201) & 0x08 ? 0 : 1;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1;
}
static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, REG_201) & 0x02 ? 0 : 1;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1;
}
static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
@ -67,7 +69,8 @@ static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, REG_201) & 0x01 ? 0 : 1;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_READ_DONE ? 0 : 1;
}
static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
@ -89,7 +92,8 @@ static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, REG_201) & 0x04 ? 1 : 0;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0;
}
static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
@ -111,7 +115,8 @@ static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, REG_201) & 0x10 ? 1 : 0;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_BUSY ? 1 : 0;
}
static int i2c_wait_done(struct i2c_adapter *i2c_adap)
@ -139,19 +144,14 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
dprintk(4, "%s()\n", __func__);
au0828_write(dev, REG_2FF, 0x01);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
/* FIXME: There is a problem with i2c communications with xc5000 that
requires us to slow down the i2c clock until we have a better
strategy (such as using the secondary i2c bus to do firmware
loading */
if ((msg->addr << 1) == 0xc2)
au0828_write(dev, REG_202, 0x40);
else
au0828_write(dev, REG_202, 0x07);
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
dev->board.i2c_clk_divider);
/* Hardware needs 8 bit addresses */
au0828_write(dev, REG_203, msg->addr << 1);
au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
dprintk(4, "SEND: %02x\n", msg->addr);
@ -163,7 +163,9 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
actual bytes to the bus, just do a read check. This is
consistent with how I saw i2c device checking done in the
USB trace of the Windows driver */
au0828_write(dev, REG_200, 0x20);
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (!i2c_wait_done(i2c_adap))
return -EIO;
@ -177,7 +179,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
dprintk(4, " %02x\n", msg->buf[i]);
au0828_write(dev, REG_205, msg->buf[i]);
au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]);
strobe++;
i++;
@ -186,9 +188,12 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
/* Strobe the byte into the bus */
if (i < msg->len)
au0828_write(dev, REG_200, 0x41);
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_WRITE |
AU0828_I2C_TRIGGER_HOLD);
else
au0828_write(dev, REG_200, 0x01);
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_WRITE);
/* Reset strobe trigger */
strobe = 0;
@ -216,25 +221,22 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
dprintk(4, "%s()\n", __func__);
au0828_write(dev, REG_2FF, 0x01);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
/* FIXME: There is a problem with i2c communications with xc5000 that
requires us to slow down the i2c clock until we have a better
strategy (such as using the secondary i2c bus to do firmware
loading */
if ((msg->addr << 1) == 0xc2)
au0828_write(dev, REG_202, 0x40);
else
au0828_write(dev, REG_202, 0x07);
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
dev->board.i2c_clk_divider);
/* Hardware needs 8 bit addresses */
au0828_write(dev, REG_203, msg->addr << 1);
au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
dprintk(4, " RECV:\n");
/* Deal with i2c_scan */
if (msg->len == 0) {
au0828_write(dev, REG_200, 0x20);
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (i2c_wait_read_ack(i2c_adap))
return -EIO;
return 0;
@ -245,14 +247,18 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
i++;
if (i < msg->len)
au0828_write(dev, REG_200, 0x60);
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ |
AU0828_I2C_TRIGGER_HOLD);
else
au0828_write(dev, REG_200, 0x20);
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (!i2c_wait_read_done(i2c_adap))
return -EIO;
msg->buf[i-1] = au0828_read(dev, REG_209) & 0xff;
msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) &
0xff;
dprintk(4, " %02x\n", msg->buf[i-1]);
}

View File

@ -30,15 +30,36 @@
#define AU0828_SENSORCTRL_100 0x100
#define AU0828_SENSORCTRL_VBI_103 0x103
#define REG_200 0x200
#define REG_201 0x201
#define REG_202 0x202
#define REG_203 0x203
#define REG_205 0x205
#define REG_209 0x209
#define REG_2FF 0x2ff
/* I2C registers */
#define AU0828_I2C_TRIGGER_200 0x200
#define AU0828_I2C_STATUS_201 0x201
#define AU0828_I2C_CLK_DIVIDER_202 0x202
#define AU0828_I2C_DEST_ADDR_203 0x203
#define AU0828_I2C_WRITE_FIFO_205 0x205
#define AU0828_I2C_READ_FIFO_209 0x209
#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff
/* Audio registers */
#define AU0828_AUDIOCTRL_50C 0x50C
#define REG_600 0x600
/*********************************************************************/
/* Here are constants for values associated with the above registers */
/* I2C Trigger (Reg 0x200) */
#define AU0828_I2C_TRIGGER_WRITE 0x01
#define AU0828_I2C_TRIGGER_READ 0x20
#define AU0828_I2C_TRIGGER_HOLD 0x40
/* I2C Status (Reg 0x201) */
#define AU0828_I2C_STATUS_READ_DONE 0x01
#define AU0828_I2C_STATUS_NO_READ_ACK 0x02
#define AU0828_I2C_STATUS_WRITE_DONE 0x04
#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08
#define AU0828_I2C_STATUS_BUSY 0x10
/* I2C Clock Divider (Reg 0x202) */
#define AU0828_I2C_CLK_250KHZ 0x07
#define AU0828_I2C_CLK_100KHZ 0x14
#define AU0828_I2C_CLK_30KHZ 0x40

View File

@ -1100,7 +1100,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
have to make the au0828 bridge adjust the size of its capture
buffer, which is currently hardcoded at 720x480 */
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_std, *norm);
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm);
return 0;
}
@ -1154,7 +1154,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
struct au0828_fh *fh = priv;
struct au0828_dev *dev = fh->dev;
int i;
struct v4l2_routing route;
dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
index);
@ -1180,9 +1179,8 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
break;
}
route.input = AUVI_INPUT(index).vmux;
route.output = 0;
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, &route);
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
AUVI_INPUT(index).vmux, 0, 0);
for (i = 0; i < AU0828_MAX_INPUT; i++) {
int enable = 0;
@ -1205,8 +1203,8 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
}
}
route.input = AUVI_INPUT(index).amux;
v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, &route);
v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
AUVI_INPUT(index).amux, 0, 0);
return 0;
}

View File

@ -81,6 +81,7 @@ struct au0828_board {
char *name;
unsigned int tuner_type;
unsigned char tuner_addr;
unsigned char i2c_clk_divider;
struct au0828_input input[AU0828_MAX_INPUT];
};

View File

@ -292,21 +292,22 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
return 0;
}
static int bt819_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int bt819_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct bt819 *decoder = to_bt819(sd);
v4l2_dbg(1, debug, sd, "set input %x\n", route->input);
v4l2_dbg(1, debug, sd, "set input %x\n", input);
if (route->input < 0 || route->input > 7)
if (input < 0 || input > 7)
return -EINVAL;
if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
v4l2_err(sd, "no notify found!\n");
if (decoder->input != route->input) {
if (decoder->input != input) {
v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
decoder->input = route->input;
decoder->input = input;
/* select mode */
if (decoder->input == 0) {
bt819_setbit(decoder, 0x0b, 6, 0);
@ -444,9 +445,6 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = {
.g_ctrl = bt819_g_ctrl,
.s_ctrl = bt819_s_ctrl,
.queryctrl = bt819_queryctrl,
};
static const struct v4l2_subdev_tuner_ops bt819_tuner_ops = {
.s_std = bt819_s_std,
};
@ -459,7 +457,6 @@ static const struct v4l2_subdev_video_ops bt819_video_ops = {
static const struct v4l2_subdev_ops bt819_ops = {
.core = &bt819_core_ops,
.tuner = &bt819_tuner_ops,
.video = &bt819_video_ops,
};

View File

@ -142,16 +142,17 @@ static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
return 0;
}
static int bt856_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int bt856_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct bt856 *encoder = to_bt856(sd);
v4l2_dbg(1, debug, sd, "set input %d\n", route->input);
v4l2_dbg(1, debug, sd, "set input %d\n", input);
/* We only have video bus.
* route->input= 0: input is from bt819
* route->input= 1: input is from ZR36060 */
switch (route->input) {
* input= 0: input is from bt819
* input= 1: input is from ZR36060 */
switch (input) {
case 0:
bt856_setbit(encoder, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1);

View File

@ -99,7 +99,8 @@ static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
return 0;
}
static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int bt866_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
static const __u8 init[] = {
0xc8, 0xcc, /* CRSCALE */
@ -137,7 +138,7 @@ static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *ro
val = encoder->reg[0xdc];
if (route->input == 0)
if (input == 0)
val |= 0x40; /* CBSWAP */
else
val &= ~0x40; /* !CBSWAP */
@ -145,15 +146,15 @@ static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *ro
bt866_write(encoder, 0xdc, val);
val = encoder->reg[0xcc];
if (route->input == 2)
if (input == 2)
val |= 0x01; /* OSDBAR */
else
val &= ~0x01; /* !OSDBAR */
bt866_write(encoder, 0xcc, val);
v4l2_dbg(1, debug, sd, "set input %d\n", route->input);
v4l2_dbg(1, debug, sd, "set input %d\n", input);
switch (route->input) {
switch (input) {
case 0:
case 1:
case 2:

View File

@ -3324,17 +3324,6 @@ void __devinit bttv_init_card1(struct bttv *btv)
/* initialization part two -- after registering i2c bus */
void __devinit bttv_init_card2(struct bttv *btv)
{
static const unsigned short tvaudio_addrs[] = {
I2C_ADDR_TDA8425 >> 1,
I2C_ADDR_TEA6300 >> 1,
I2C_ADDR_TEA6420 >> 1,
I2C_ADDR_TDA9840 >> 1,
I2C_ADDR_TDA985x_L >> 1,
I2C_ADDR_TDA985x_H >> 1,
I2C_ADDR_TDA9874 >> 1,
I2C_ADDR_PIC16C54 >> 1,
I2C_CLIENT_END
};
int addr=ADDR_UNSET;
btv->tuner_type = UNSET;
@ -3512,12 +3501,15 @@ void __devinit bttv_init_card2(struct bttv *btv)
/* Load tuner module before issuing tuner config call! */
if (bttv_tvcards[btv->c.type].has_radio)
v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_RADIO));
v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, "tuner",
"tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, "tuner",
"tuner", v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_RADIO));
v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
tun_setup.type = btv->tuner_type;
@ -3570,8 +3562,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
};
struct v4l2_subdev *sd;
sd = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"saa6588", "saa6588", addrs);
sd = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "saa6588", "saa6588", addrs);
btv->has_saa6588 = (sd != NULL);
}
@ -3595,8 +3587,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
I2C_CLIENT_END
};
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"msp3400", "msp3400", addrs);
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400", addrs);
if (btv->sd_msp34xx)
return;
goto no_audio;
@ -3609,16 +3601,16 @@ void __devinit bttv_init_card2(struct bttv *btv)
I2C_CLIENT_END
};
if (v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"tda7432", "tda7432", addrs))
if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tda7432", "tda7432", addrs))
return;
goto no_audio;
}
case 3: {
/* The user specified that we should probe for tvaudio */
btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"tvaudio", "tvaudio", tvaudio_addrs);
btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs());
if (btv->sd_tvaudio)
return;
goto no_audio;
@ -3637,21 +3629,13 @@ void __devinit bttv_init_card2(struct bttv *btv)
it really is a msp3400, so it will return NULL when the device
found is really something else (e.g. a tea6300). */
if (!bttv_tvcards[btv->c.type].no_msp34xx) {
static const unsigned short addrs[] = {
I2C_ADDR_MSP3400 >> 1,
I2C_CLIENT_END
};
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"msp3400", "msp3400", addrs);
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400",
I2C_ADDR_MSP3400 >> 1);
} else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
static const unsigned short addrs[] = {
I2C_ADDR_MSP3400_ALT >> 1,
I2C_CLIENT_END
};
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"msp3400", "msp3400", addrs);
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400",
I2C_ADDR_MSP3400_ALT >> 1);
}
/* If we found a msp34xx, then we're done. */
@ -3665,14 +3649,14 @@ void __devinit bttv_init_card2(struct bttv *btv)
I2C_CLIENT_END
};
if (v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"tda7432", "tda7432", addrs))
if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tda7432", "tda7432", addrs))
return;
}
/* Now see if we can find one of the tvaudio devices. */
btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
"tvaudio", "tvaudio", tvaudio_addrs);
btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs());
if (btv->sd_tvaudio)
return;

View File

@ -1198,7 +1198,7 @@ audio_mux(struct bttv *btv, int input, int mute)
ctrl.value = btv->mute;
bttv_call_all(btv, core, s_ctrl, &ctrl);
if (btv->sd_msp34xx) {
struct v4l2_routing route;
u32 in;
/* Note: the inputs tuner/radio/extern/intern are translated
to msp routings. This assumes common behavior for all msp3400
@ -1207,11 +1207,11 @@ audio_mux(struct bttv *btv, int input, int mute)
For now this is sufficient. */
switch (input) {
case TVAUDIO_INPUT_RADIO:
route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
break;
case TVAUDIO_INPUT_EXTERN:
route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
break;
case TVAUDIO_INPUT_INTERN:
@ -1220,7 +1220,7 @@ audio_mux(struct bttv *btv, int input, int mute)
input is the BTTV_BOARD_AVERMEDIA98. I wonder how
that was tested. My guess is that the whole INTERN
input does not work. */
route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
break;
case TVAUDIO_INPUT_TUNER:
@ -1229,21 +1229,18 @@ audio_mux(struct bttv *btv, int input, int mute)
is the only difference between the VOODOOTV_FM
and VOODOOTV_200 */
if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
else
route.input = MSP_INPUT_DEFAULT;
in = MSP_INPUT_DEFAULT;
break;
}
route.output = MSP_OUTPUT_DEFAULT;
v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing, &route);
v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
in, MSP_OUTPUT_DEFAULT, 0);
}
if (btv->sd_tvaudio) {
struct v4l2_routing route;
route.input = input;
route.output = 0;
v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, &route);
v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
input, 0, 0);
}
return 0;
}
@ -1329,7 +1326,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
break;
}
id = tvnorm->v4l2_id;
bttv_call_all(btv, tuner, s_std, id);
bttv_call_all(btv, core, s_std, id);
return 0;
}

View File

@ -26,7 +26,7 @@
#define _BTTVP_H_
#include <linux/version.h>
#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,17)
#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,18)
#include <linux/types.h>
#include <linux/wait.h>

View File

@ -1954,7 +1954,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
goto out_freeirq;
cam->sensor_addr = 0x42;
cam->sensor = v4l2_i2c_new_subdev(&cam->i2c_adapter,
cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter,
"ov7670", "ov7670", cam->sensor_addr);
if (cam->sensor == NULL) {
ret = -ENODEV;

View File

@ -53,14 +53,15 @@ static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg)
return i2c_smbus_read_byte_data(client, reg);
}
static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int cs5345_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
if ((route->input & 0xf) > 6) {
v4l2_err(sd, "Invalid input %d.\n", route->input);
if ((input & 0xf) > 6) {
v4l2_err(sd, "Invalid input %d.\n", input);
return -EINVAL;
}
cs5345_write(sd, 0x09, route->input & 0xf);
cs5345_write(sd, 0x05, route->input & 0xf0);
cs5345_write(sd, 0x09, input & 0xf);
cs5345_write(sd, 0x05, input & 0xf0);
return 0;
}

View File

@ -58,17 +58,18 @@ static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg)
return i2c_smbus_read_byte_data(client, reg);
}
static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int cs53l32a_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
/* There are 2 physical inputs, but the second input can be
placed in two modes, the first mode bypasses the PGA (gain),
the second goes through the PGA. Hence there are three
possible inputs to choose from. */
if (route->input > 2) {
v4l2_err(sd, "Invalid input %d.\n", route->input);
if (input > 2) {
v4l2_err(sd, "Invalid input %d.\n", input);
return -EINVAL;
}
cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4));
cs53l32a_write(sd, 0x01, 0x01 + (input << 4));
return 0;
}

View File

@ -33,7 +33,6 @@
int cx18_audio_set_io(struct cx18 *cx)
{
const struct cx18_card_audio_input *in;
struct v4l2_routing route;
u32 val;
int err;
@ -44,13 +43,11 @@ int cx18_audio_set_io(struct cx18 *cx)
in = &cx->card->audio_inputs[cx->audio_input];
/* handle muxer chips */
route.input = in->muxer_input;
route.output = 0;
v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route);
v4l2_subdev_call(cx->sd_extmux, audio, s_routing,
in->audio_input, 0, 0);
route.input = in->audio_input;
err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
audio, s_routing, &route);
audio, s_routing, in->audio_input, 0, 0);
if (err)
return err;

View File

@ -202,44 +202,43 @@ static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
}
static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
{
struct cx18 *cx = v4l2_get_subdevdata(sd);
/*
* The crystal freq used in calculations in this driver will be
* 28.636360 MHz.
* Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
*/
/*
* VDCLK Integer = 0x0f, Post Divider = 0x04
* AIMCLK Integer = 0x0e, Post Divider = 0x16
*/
cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
/* VDCLK Fraction = 0x2be2fe */
/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
/* AIMCLK Fraction = 0x05227ad */
/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
return 0;
}
static int cx18_av_load_fw(struct v4l2_subdev *sd)
{
struct cx18_av_state *state = to_cx18_av_state(sd);
struct cx18 *cx = v4l2_get_subdevdata(sd);
switch (val) {
case CX18_AV_INIT_PLLS:
/*
* The crystal freq used in calculations in this driver will be
* 28.636360 MHz.
* Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
*/
/*
* VDCLK Integer = 0x0f, Post Divider = 0x04
* AIMCLK Integer = 0x0e, Post Divider = 0x16
*/
cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
/* VDCLK Fraction = 0x2be2fe */
/* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
/* AIMCLK Fraction = 0x05227ad */
/* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
break;
case CX18_AV_INIT_NORMAL:
default:
if (!state->is_initialized) {
/* initialize on first use */
state->is_initialized = 1;
cx18_av_initialize(cx);
}
break;
if (!state->is_initialized) {
/* initialize on first use */
state->is_initialized = 1;
cx18_av_initialize(cx);
}
return 0;
}
@ -548,19 +547,19 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
}
static int cx18_av_s_video_routing(struct v4l2_subdev *sd,
const struct v4l2_routing *route)
u32 input, u32 output, u32 config)
{
struct cx18_av_state *state = to_cx18_av_state(sd);
struct cx18 *cx = v4l2_get_subdevdata(sd);
return set_input(cx, route->input, state->aud_input);
return set_input(cx, input, state->aud_input);
}
static int cx18_av_s_audio_routing(struct v4l2_subdev *sd,
const struct v4l2_routing *route)
u32 input, u32 output, u32 config)
{
struct cx18_av_state *state = to_cx18_av_state(sd);
struct cx18 *cx = v4l2_get_subdevdata(sd);
return set_input(cx, state->vid_input, route->input);
return set_input(cx, state->vid_input, input);
}
static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
@ -1185,10 +1184,12 @@ static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
.g_chip_ident = cx18_av_g_chip_ident,
.log_status = cx18_av_log_status,
.init = cx18_av_init,
.load_fw = cx18_av_load_fw,
.reset = cx18_av_reset,
.queryctrl = cx18_av_queryctrl,
.g_ctrl = cx18_av_g_ctrl,
.s_ctrl = cx18_av_s_ctrl,
.s_std = cx18_av_s_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx18_av_g_register,
.s_register = cx18_av_s_register,
@ -1200,7 +1201,6 @@ static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = {
.s_frequency = cx18_av_s_frequency,
.g_tuner = cx18_av_g_tuner,
.s_tuner = cx18_av_s_tuner,
.s_std = cx18_av_s_std,
};
static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {

View File

@ -328,11 +328,6 @@ static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd)
return container_of(sd, struct cx18_av_state, sd);
}
enum cx18_av_subdev_init_arg {
CX18_AV_INIT_NORMAL = 0,
CX18_AV_INIT_PLLS = 1,
};
/* ----------------------------------------------------------------------- */
/* cx18_av-core.c */
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);

View File

@ -810,7 +810,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
CX18_ERR("Could not register A/V decoder subdevice\n");
goto free_map;
}
cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0);
/* Initialize GPIO Reset Controller to do chip resets during i2c init */
if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
@ -1028,7 +1028,7 @@ int cx18_init_on_first_open(struct cx18 *cx)
cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
/* Init the A/V decoder, if it hasn't been already */
v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL);
v4l2_subdev_call(cx->sd_av, core, load_fw);
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;

View File

@ -608,7 +608,7 @@ int cx18_v4l2_close(struct file *filp)
/* Mark that the radio is no longer in use */
clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
/* Switch tuner to TV */
cx18_call_all(cx, tuner, s_std, cx->std);
cx18_call_all(cx, core, s_std, cx->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
cx18_audio_set_io(cx);
if (atomic_read(&cx->ana_capturing) > 0) {

View File

@ -156,12 +156,12 @@ static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
}
static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
const struct v4l2_routing *route)
u32 input, u32 output, u32 config)
{
struct cx18 *cx = v4l2_get_subdevdata(sd);
u32 data;
switch (route->input) {
switch (input) {
case 0:
data = cx->card->gpio_audio_input.tuner;
break;
@ -180,10 +180,10 @@ static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
.log_status = gpiomux_log_status,
.s_std = gpiomux_s_std,
};
static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
.s_std = gpiomux_s_std,
.s_radio = gpiomux_s_radio,
};

View File

@ -100,16 +100,16 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
if (hw == CX18_HW_TUNER) {
/* special tuner group handling */
sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
cx->card_i2c->radio);
sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev,
adap, mod, type, cx->card_i2c->radio);
if (sd != NULL)
sd->grp_id = hw;
sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
cx->card_i2c->demod);
sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev,
adap, mod, type, cx->card_i2c->demod);
if (sd != NULL)
sd->grp_id = hw;
sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
cx->card_i2c->tv);
sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev,
adap, mod, type, cx->card_i2c->tv);
if (sd != NULL)
sd->grp_id = hw;
return sd != NULL ? 0 : -1;
@ -120,7 +120,7 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
return -1;
/* It's an I2C device other than an analog tuner */
sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx]);
if (sd != NULL)
sd->grp_id = hw;
return sd != NULL ? 0 : -1;

View File

@ -705,7 +705,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
(unsigned long long) cx->std);
/* Tuner */
cx18_call_all(cx, tuner, s_std, cx->std);
cx18_call_all(cx, core, s_std, cx->std);
return 0;
}
@ -926,16 +926,6 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
switch (cmd) {
case VIDIOC_INT_S_AUDIO_ROUTING: {
struct v4l2_routing *route = arg;
CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
route->input, route->output);
cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing,
route);
break;
}
case VIDIOC_INT_RESET: {
u32 val = *(u32 *)arg;

View File

@ -25,20 +25,8 @@
void cx18_video_set_io(struct cx18 *cx)
{
struct v4l2_routing route;
int inp = cx->active_input;
u32 type;
route.input = cx->card->video_inputs[inp].video_input;
route.output = 0;
v4l2_subdev_call(cx->sd_av, video, s_routing, &route);
type = cx->card->video_inputs[inp].video_type;
if (type == CX18_CARD_INPUT_VID_TUNER)
route.input = 0; /* Tuner */
else if (type < CX18_CARD_INPUT_COMPOSITE1)
route.input = 2; /* S-Video */
else
route.input = 1; /* Composite */
v4l2_subdev_call(cx->sd_av, video, s_routing,
cx->card->video_inputs[inp].video_input, 0, 0);
}

View File

@ -0,0 +1,35 @@
config VIDEO_CX231XX
tristate "Conexant cx231xx USB video capture support"
depends on VIDEO_DEV && I2C && INPUT
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
select VIDEOBUF_VMALLOC
select VIDEO_CX25840
select VIDEO_CX231XX_ALSA
---help---
This is a video4linux driver for Conexant 231xx USB based TV cards.
To compile this driver as a module, choose M here: the
module will be called cx231xx
config VIDEO_CX231XX_ALSA
tristate "Conexant Cx231xx ALSA audio module"
depends on VIDEO_CX231XX && SND
select SND_PCM
---help---
This is an ALSA driver for Cx231xx USB based TV cards.
To compile this driver as a module, choose M here: the
module will be called cx231xx-alsa
config VIDEO_CX231XX_DVB
tristate "DVB/ATSC Support for Cx231xx based TV cards"
depends on VIDEO_CX231XX && DVB_CORE
select VIDEOBUF_DVB
select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB cards based on the
Conexant cx231xx chips.

View File

@ -0,0 +1,14 @@
cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \
cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o
cx231xx-alsa-objs := cx231xx-audio.o
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends

View File

@ -0,0 +1,586 @@
/*
* Conexant Cx231xx audio extension
*
* Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
* Based on em28xx driver
*
*
* 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/usb.h>
#include <linux/init.h>
#include <linux/sound.h>
#include <linux/spinlock.h>
#include <linux/soundcard.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <media/v4l2-common.h>
#include "cx231xx.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
#define dprintk(fmt, arg...) do { \
if (debug) \
printk(KERN_INFO "cx231xx-audio %s: " fmt, \
__func__, ##arg); \
} while (0)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
{
int i;
dprintk("Stopping isoc\n");
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
if (dev->adev.urb[i]) {
if (!irqs_disabled())
usb_kill_urb(dev->adev.urb[i]);
else
usb_unlink_urb(dev->adev.urb[i]);
usb_free_urb(dev->adev.urb[i]);
dev->adev.urb[i] = NULL;
kfree(dev->adev.transfer_buffer[i]);
dev->adev.transfer_buffer[i] = NULL;
}
}
return 0;
}
static void cx231xx_audio_isocirq(struct urb *urb)
{
struct cx231xx *dev = urb->context;
int i;
unsigned int oldptr;
int period_elapsed = 0;
int status;
unsigned char *cp;
unsigned int stride;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dprintk("urb completition error %d.\n", urb->status);
break;
}
if (dev->adev.capture_pcm_substream) {
substream = dev->adev.capture_pcm_substream;
runtime = substream->runtime;
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
int length = urb->iso_frame_desc[i].actual_length /
stride;
cp = (unsigned char *)urb->transfer_buffer +
urb->iso_frame_desc[i].offset;
if (!length)
continue;
oldptr = dev->adev.hwptr_done_capture;
if (oldptr + length >= runtime->buffer_size) {
unsigned int cnt;
cnt = runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, cp,
cnt * stride);
memcpy(runtime->dma_area, cp + cnt * stride,
length * stride - cnt * stride);
} else {
memcpy(runtime->dma_area + oldptr * stride, cp,
length * stride);
}
snd_pcm_stream_lock(substream);
dev->adev.hwptr_done_capture += length;
if (dev->adev.hwptr_done_capture >=
runtime->buffer_size)
dev->adev.hwptr_done_capture -=
runtime->buffer_size;
dev->adev.capture_transfer_done += length;
if (dev->adev.capture_transfer_done >=
runtime->period_size) {
dev->adev.capture_transfer_done -=
runtime->period_size;
period_elapsed = 1;
}
snd_pcm_stream_unlock(substream);
}
if (period_elapsed)
snd_pcm_period_elapsed(substream);
}
urb->status = 0;
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
status);
}
return;
}
static int cx231xx_init_audio_isoc(struct cx231xx *dev)
{
int i, errCode;
int sb_size;
cx231xx_info("%s: Starting AUDIO transfers\n", __func__);
sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
struct urb *urb;
int j, k;
dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
if (!dev->adev.transfer_buffer[i])
return -ENOMEM;
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
cx231xx_errdev("usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
}
return -ENOMEM;
}
urb->dev = dev->udev;
urb->context = dev;
urb->pipe = usb_rcvisocpipe(dev->udev,
dev->adev.end_point_addr);
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = dev->adev.transfer_buffer[i];
urb->interval = 1;
urb->complete = cx231xx_audio_isocirq;
urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS;
urb->transfer_buffer_length = sb_size;
for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS;
j++, k += dev->adev.max_pkt_size) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
}
dev->adev.urb[i] = urb;
}
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
if (errCode < 0) {
cx231xx_isoc_audio_deinit(dev);
return errCode;
}
}
return errCode;
}
static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg)
{
dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
"stop" : "start");
switch (cmd) {
case CX231XX_CAPTURE_STREAM_EN:
if (dev->adev.capture_stream == STREAM_OFF && arg == 1) {
dev->adev.capture_stream = STREAM_ON;
cx231xx_init_audio_isoc(dev);
} else if (dev->adev.capture_stream == STREAM_ON && arg == 0) {
dev->adev.capture_stream = STREAM_OFF;
cx231xx_isoc_audio_deinit(dev);
} else {
cx231xx_errdev("An underrun very likely occurred. "
"Ignoring it.\n");
}
return 0;
default:
return -EINVAL;
}
}
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
dprintk("Allocating vbuffer\n");
if (runtime->dma_area) {
if (runtime->dma_bytes > size)
return 0;
vfree(runtime->dma_area);
}
runtime->dma_area = vmalloc(size);
if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
return 0;
}
static struct snd_pcm_hardware snd_cx231xx_hw_capture = {
.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
.period_bytes_min = 64, /* 12544/2, */
.period_bytes_max = 12544,
.periods_min = 2,
.periods_max = 98, /* 12544, */
};
static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
dprintk("opening device and trying to acquire exclusive lock\n");
if (!dev) {
cx231xx_errdev("BUG: cx231xx can't find device struct."
" Can't proceed with open\n");
return -ENODEV;
}
/* Sets volume, mute, etc */
dev->mute = 0;
/* set alternate setting for audio interface */
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
if (ret < 0) {
cx231xx_errdev("failed to set alternate setting !\n");
return ret;
}
/* inform hardware to start streaming */
ret = cx231xx_capture_start(dev, 1, Audio);
runtime->hw = snd_cx231xx_hw_capture;
mutex_lock(&dev->lock);
dev->adev.users++;
mutex_unlock(&dev->lock);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
dev->adev.capture_pcm_substream = substream;
runtime->private_data = dev;
return 0;
}
static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
{
int ret;
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dprintk("closing device\n");
/* set alternate setting for audio interface */
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
if (ret < 0) {
cx231xx_errdev("failed to set alternate setting !\n");
return ret;
}
/* inform hardware to start streaming */
ret = cx231xx_capture_start(dev, 0, Audio);
dev->mute = 1;
mutex_lock(&dev->lock);
dev->adev.users--;
mutex_unlock(&dev->lock);
if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
dprintk("audio users: %d\n", dev->adev.users);
dprintk("disabling audio stream!\n");
dev->adev.shutdown = 0;
dprintk("released lock\n");
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0);
}
return 0;
}
static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
unsigned int channels, rate, format;
int ret;
dprintk("Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
/* TODO: set up cx231xx audio chip to deliver the correct audio format,
current default is 48000hz multiplexed => 96000hz mono
which shouldn't matter since analogue TV only supports mono */
return 0;
}
static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
dprintk("Stop capture, if needed\n");
if (dev->adev.capture_stream == STREAM_ON)
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
return 0;
}
static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
int retval;
dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
"start" : "stop");
spin_lock(&dev->adev.slock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN,
CX231XX_START_AUDIO);
retval = 0;
break;
case SNDRV_PCM_TRIGGER_STOP:
cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
retval = 0;
break;
default:
retval = -EINVAL;
}
spin_unlock(&dev->adev.slock);
return retval;
}
static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
*substream)
{
struct cx231xx *dev;
unsigned long flags;
snd_pcm_uframes_t hwptr_done;
dev = snd_pcm_substream_chip(substream);
spin_lock_irqsave(&dev->adev.slock, flags);
hwptr_done = dev->adev.hwptr_done_capture;
spin_unlock_irqrestore(&dev->adev.slock, flags);
return hwptr_done;
}
static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
unsigned long offset)
{
void *pageptr = subs->runtime->dma_area + offset;
return vmalloc_to_page(pageptr);
}
static struct snd_pcm_ops snd_cx231xx_pcm_capture = {
.open = snd_cx231xx_capture_open,
.close = snd_cx231xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cx231xx_hw_capture_params,
.hw_free = snd_cx231xx_hw_capture_free,
.prepare = snd_cx231xx_prepare,
.trigger = snd_cx231xx_capture_trigger,
.pointer = snd_cx231xx_capture_pointer,
.page = snd_pcm_get_vmalloc_page,
};
static int cx231xx_audio_init(struct cx231xx *dev)
{
struct cx231xx_audio *adev = &dev->adev;
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
int err;
struct usb_interface *uif;
int i, isoc_pipe = 0;
if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
cx231xx_info("cx231xx-audio.c: probing for cx231xx "
"non standard usbaudio\n");
err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE,
0, &card);
if (err < 0)
return err;
spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
if (err < 0) {
snd_card_free(card);
return err;
}
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_cx231xx_pcm_capture);
pcm->info_flags = 0;
pcm->private_data = dev;
strcpy(pcm->name, "Conexant cx231xx Capture");
strcpy(card->driver, "Conexant cx231xx Audio");
strcpy(card->shortname, "Cx231xx Audio");
strcpy(card->longname, "Conexant cx231xx Audio");
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
adev->sndcard = card;
adev->udev = dev->udev;
/* compute alternate max packet sizes for Audio */
uif =
dev->udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
audio_index + 1];
adev->end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress);
adev->num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
adev->end_point_addr, adev->num_alt);
adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
if (adev->alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
return -ENOMEM;
}
for (i = 0; i < adev->num_alt; i++) {
u16 tmp =
le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
wMaxPacketSize);
adev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
adev->alt_max_pkt_size[i]);
}
return 0;
}
static int cx231xx_audio_fini(struct cx231xx *dev)
{
if (dev == NULL)
return 0;
if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
the device is expecting the snd-usb-audio module or
doesn't have analog audio support at all) */
return 0;
}
if (dev->adev.sndcard) {
snd_card_free(dev->adev.sndcard);
kfree(dev->adev.alt_max_pkt_size);
dev->adev.sndcard = NULL;
}
return 0;
}
static struct cx231xx_ops audio_ops = {
.id = CX231XX_AUDIO,
.name = "Cx231xx Audio Extension",
.init = cx231xx_audio_init,
.fini = cx231xx_audio_fini,
};
static int __init cx231xx_alsa_register(void)
{
return cx231xx_register_extension(&audio_ops);
}
static void __exit cx231xx_alsa_unregister(void)
{
cx231xx_unregister_extension(&audio_ops);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
MODULE_DESCRIPTION("Cx231xx Audio driver");
module_init(cx231xx_alsa_register);
module_exit(cx231xx_alsa_unregister);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,914 @@
/*
cx231xx-cards.c - driver for Conexant Cx23100/101/102
USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
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/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/usb.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
#include <media/cx25840.h>
#include "xc5000.h"
#include "cx231xx.h"
static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");
static unsigned int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */
static unsigned long cx231xx_devused;
/*
* Reset sequences for analog/digital modes
*/
static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = {
{0x03, 0x01, 10},
{0x03, 0x00, 30},
{0x03, 0x01, 10},
{-1, -1, -1},
};
/*
* Board definitions
*/
struct cx231xx_board cx231xx_boards[] = {
[CX231XX_BOARD_UNKNOWN] = {
.name = "Unknown CX231xx video grabber",
.tuner_type = TUNER_ABSENT,
.input = {{
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
.gpio = 0,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}
},
},
[CX231XX_BOARD_CNXT_RDE_250] = {
.name = "Conexant Hybrid TV - RDE250",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.tuner_gpio = RDE250_XCV_TUNER,
.tuner_sif_gpio = 0x05,
.tuner_scl_gpio = 0x1a,
.tuner_sda_gpio = 0x1b,
.decoder = CX231XX_AVDECODER,
.demod_xfer_mode = 0,
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = 1,
.demod_i2c_master = 2,
.has_dvb = 1,
.demod_addr = 0x02,
.norm = V4L2_STD_PAL,
.input = {{
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
.gpio = 0,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}
},
},
[CX231XX_BOARD_CNXT_RDU_250] = {
.name = "Conexant Hybrid TV - RDU250",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.tuner_gpio = RDE250_XCV_TUNER,
.tuner_sif_gpio = 0x05,
.tuner_scl_gpio = 0x1a,
.tuner_sda_gpio = 0x1b,
.decoder = CX231XX_AVDECODER,
.demod_xfer_mode = 0,
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = 1,
.demod_i2c_master = 2,
.has_dvb = 1,
.demod_addr = 0x32,
.norm = V4L2_STD_NTSC,
.input = {{
.type = CX231XX_VMUX_TELEVISION,
.vmux = CX231XX_VIN_3_1,
.amux = CX231XX_AMUX_VIDEO,
.gpio = 0,
}, {
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}, {
.type = CX231XX_VMUX_SVIDEO,
.vmux = CX231XX_VIN_1_1 |
(CX231XX_VIN_1_2 << 8) |
CX25840_SVIDEO_ON,
.amux = CX231XX_AMUX_LINE_IN,
.gpio = 0,
}
},
},
};
const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
/* table of devices that work with this driver */
struct usb_device_id cx231xx_id_table[] = {
{USB_DEVICE(0x0572, 0x5A3C),
.driver_info = CX231XX_BOARD_UNKNOWN},
{USB_DEVICE(0x0572, 0x58A2),
.driver_info = CX231XX_BOARD_CNXT_RDE_250},
{USB_DEVICE(0x0572, 0x58A1),
.driver_info = CX231XX_BOARD_CNXT_RDU_250},
{},
};
MODULE_DEVICE_TABLE(usb, cx231xx_id_table);
/* cx231xx_tuner_callback
* will be used to reset XC5000 tuner using GPIO pin
*/
int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
{
int rc = 0;
struct cx231xx *dev = ptr;
if (dev->tuner_type == TUNER_XC5000) {
if (command == XC5000_TUNER_RESET) {
cx231xx_info
("Tuner CB: RESET: cmd %d : tuner type %d \n",
command, dev->tuner_type);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
1);
msleep(10);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
0);
msleep(330);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
1);
msleep(10);
}
}
return rc;
}
EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
static inline void cx231xx_set_model(struct cx231xx *dev)
{
memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
}
/* Since cx231xx_pre_card_setup() requires a proper dev->model,
* this won't work for boards with generic PCI IDs
*/
void cx231xx_pre_card_setup(struct cx231xx *dev)
{
cx231xx_set_model(dev);
cx231xx_info("Identified as %s (card=%d)\n",
dev->board.name, dev->model);
/* set the direction for GPIO pins */
cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
/* request some modules if any required */
/* reset the Tuner */
cx231xx_gpio_set(dev, dev->board.tuner_gpio);
/* set the mode to Analog mode initially */
cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
/* Unlock device */
/* cx231xx_set_mode(dev, CX231XX_SUSPEND); */
}
static void cx231xx_config_tuner(struct cx231xx *dev)
{
struct tuner_setup tun_setup;
struct v4l2_frequency f;
if (dev->tuner_type == TUNER_ABSENT)
return;
tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
tun_setup.type = dev->tuner_type;
tun_setup.addr = dev->tuner_addr;
tun_setup.tuner_callback = cx231xx_tuner_callback;
tuner_call(dev, tuner, s_type_addr, &tun_setup);
#if 0
if (tun_setup.type == TUNER_XC5000) {
static struct xc2028_ctrl ctrl = {
.fname = XC5000_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = 0;
};
struct v4l2_priv_tun_config cfg = {
.tuner = dev->tuner_type,
.priv = &ctrl,
};
tuner_call(dev, tuner, s_config, &cfg);
}
#endif
/* configure tuner */
f.tuner = 0;
f.type = V4L2_TUNER_ANALOG_TV;
f.frequency = 9076; /* just a magic number */
dev->ctl_freq = f.frequency;
call_all(dev, tuner, s_frequency, &f);
}
/* ----------------------------------------------------------------------- */
void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir)
{
if (disable_ir) {
ir->get_key = NULL;
return;
}
/* detect & configure */
switch (dev->model) {
case CX231XX_BOARD_CNXT_RDE_250:
break;
case CX231XX_BOARD_CNXT_RDU_250:
break;
default:
break;
}
}
void cx231xx_card_setup(struct cx231xx *dev)
{
cx231xx_set_model(dev);
dev->tuner_type = cx231xx_boards[dev->model].tuner_type;
if (cx231xx_boards[dev->model].tuner_addr)
dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr;
/* request some modules */
if (dev->board.decoder == CX231XX_AVDECODER) {
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[0].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1);
if (dev->sd_cx25840 == NULL)
cx231xx_info("cx25840 subdev registration failure\n");
cx25840_call(dev, core, load_fw);
}
if (dev->board.tuner_type != TUNER_ABSENT) {
dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", 0xc2 >> 1);
if (dev->sd_tuner == NULL)
cx231xx_info("tuner subdev registration failure\n");
cx231xx_config_tuner(dev);
}
cx231xx_config_tuner(dev);
#if 0
/* TBD IR will be added later */
cx231xx_ir_init(dev);
#endif
}
/*
* cx231xx_config()
* inits registers with sane defaults
*/
int cx231xx_config(struct cx231xx *dev)
{
/* TBD need to add cx231xx specific code */
dev->mute = 1; /* maybe not the right place... */
dev->volume = 0x1f;
return 0;
}
/*
* cx231xx_config_i2c()
* configure i2c attached devices
*/
void cx231xx_config_i2c(struct cx231xx *dev)
{
/* u32 input = INPUT(dev->video_input)->vmux; */
call_all(dev, video, s_stream, 1);
}
/*
* cx231xx_realease_resources()
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconected or at module unload
*/
void cx231xx_release_resources(struct cx231xx *dev)
{
#if 0 /* TBD IR related */
if (dev->ir)
cx231xx_ir_fini(dev);
#endif
cx231xx_release_analog_resources(dev);
cx231xx_remove_from_devlist(dev);
cx231xx_dev_uninit(dev);
usb_put_dev(dev->udev);
/* Mark device as unused */
cx231xx_devused &= ~(1 << dev->devno);
}
/*
* cx231xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
*/
static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
int minor)
{
struct cx231xx *dev = *devhandle;
int retval = -ENOMEM;
int errCode;
unsigned int maxh, maxw;
dev->udev = udev;
mutex_init(&dev->lock);
mutex_init(&dev->ctrl_urb_lock);
mutex_init(&dev->gpio_i2c_lock);
spin_lock_init(&dev->video_mode.slock);
spin_lock_init(&dev->vbi_mode.slock);
spin_lock_init(&dev->sliced_cc_mode.slock);
init_waitqueue_head(&dev->open);
init_waitqueue_head(&dev->wait_frame);
init_waitqueue_head(&dev->wait_stream);
dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg;
dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg;
dev->cx231xx_send_usb_command = cx231xx_send_usb_command;
dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read;
dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write;
/* Query cx231xx to find what pcb config it is related to */
initialize_cx231xx(dev);
/* Cx231xx pre card setup */
cx231xx_pre_card_setup(dev);
errCode = cx231xx_config(dev);
if (errCode) {
cx231xx_errdev("error configuring device\n");
return -ENOMEM;
}
/* set default norm */
dev->norm = dev->board.norm;
/* register i2c bus */
errCode = cx231xx_dev_init(dev);
if (errCode < 0) {
cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
__func__, errCode);
return errCode;
}
/* Do board specific init */
cx231xx_card_setup(dev);
/* configure the device */
cx231xx_config_i2c(dev);
maxw = norm_maxw(dev);
maxh = norm_maxh(dev);
/* set default image size */
dev->width = maxw;
dev->height = maxh;
dev->interlaced = 0;
dev->hscale = 0;
dev->vscale = 0;
dev->video_input = 0;
errCode = cx231xx_config(dev);
if (errCode < 0) {
cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
__func__, errCode);
return errCode;
}
/* init video dma queues */
INIT_LIST_HEAD(&dev->video_mode.vidq.active);
INIT_LIST_HEAD(&dev->video_mode.vidq.queued);
/* init vbi dma queues */
INIT_LIST_HEAD(&dev->vbi_mode.vidq.active);
INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
/* Reset other chips required if they are tied up with GPIO pins */
cx231xx_add_into_devlist(dev);
retval = cx231xx_register_analog_devices(dev);
if (retval < 0) {
cx231xx_release_resources(dev);
goto fail_reg_devices;
}
cx231xx_init_extension(dev);
return 0;
fail_reg_devices:
mutex_unlock(&dev->lock);
return retval;
}
#if defined(CONFIG_MODULES) && defined(MODULE)
static void request_module_async(struct work_struct *work)
{
struct cx231xx *dev = container_of(work,
struct cx231xx, request_module_wk);
if (dev->has_alsa_audio)
request_module("cx231xx-alsa");
if (dev->board.has_dvb)
request_module("cx231xx-dvb");
}
static void request_modules(struct cx231xx *dev)
{
INIT_WORK(&dev->request_module_wk, request_module_async);
schedule_work(&dev->request_module_wk);
}
#else
#define request_modules(dev)
#endif /* CONFIG_MODULES */
/*
* cx231xx_usb_probe()
* checks for supported devices
*/
static int cx231xx_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev;
struct usb_interface *uif;
struct cx231xx *dev = NULL;
int retval = -ENODEV;
int nr = 0, ifnum;
int i, isoc_pipe = 0;
char *speed;
char descr[255] = "";
struct usb_interface *lif = NULL;
int skip_interface = 0;
struct usb_interface_assoc_descriptor *assoc_desc;
udev = usb_get_dev(interface_to_usbdev(interface));
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
if (!ifnum) {
/*
* Interface number 0 - IR interface
*/
/* Check to see next free device and mark as used */
nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
cx231xx_devused |= 1 << nr;
if (nr >= CX231XX_MAXBOARDS) {
cx231xx_err(DRIVER_NAME ": Supports only %i cx231xx boards.\n",
CX231XX_MAXBOARDS);
cx231xx_devused &= ~(1 << nr);
return -ENOMEM;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
cx231xx_err(DRIVER_NAME ": out of memory!\n");
cx231xx_devused &= ~(1 << nr);
return -ENOMEM;
}
snprintf(dev->name, 29, "cx231xx #%d", nr);
dev->devno = nr;
dev->model = id->driver_info;
dev->video_mode.alt = -1;
dev->interface_count++;
/* reset gpio dir and value */
dev->gpio_dir = 0;
dev->gpio_val = 0;
dev->xc_fw_load_done = 0;
dev->has_alsa_audio = 1;
dev->power_mode = -1;
/* 0 - vbi ; 1 -sliced cc mode */
dev->vbi_or_sliced_cc_mode = 0;
/* get maximum no.of IAD interfaces */
assoc_desc = udev->actconfig->intf_assoc[0];
dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
/* init CIR module TBD */
/* store the current interface */
lif = interface;
switch (udev->speed) {
case USB_SPEED_LOW:
speed = "1.5";
break;
case USB_SPEED_UNKNOWN:
case USB_SPEED_FULL:
speed = "12";
break;
case USB_SPEED_HIGH:
speed = "480";
break;
default:
speed = "unknown";
}
if (udev->manufacturer)
strlcpy(descr, udev->manufacturer, sizeof(descr));
if (udev->product) {
if (*descr)
strlcat(descr, " ", sizeof(descr));
strlcat(descr, udev->product, sizeof(descr));
}
if (*descr)
strlcat(descr, " ", sizeof(descr));
cx231xx_info("New device %s@ %s Mbps "
"(%04x:%04x) with %d interfaces\n",
descr,
speed,
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
dev->max_iad_interface_count);
} else {
/* Get dev structure first */
dev = usb_get_intfdata(udev->actconfig->interface[0]);
if (dev == NULL) {
cx231xx_err(DRIVER_NAME ": out of first interface!\n");
return -ENODEV;
}
/* store the interface 0 back */
lif = udev->actconfig->interface[0];
/* increment interface count */
dev->interface_count++;
/* get device number */
nr = dev->devno;
/*
* set skip interface, for all interfaces but
* interface 1 and the last one
*/
if ((ifnum != 1) && ((dev->interface_count - 1)
!= dev->max_iad_interface_count))
skip_interface = 1;
if (ifnum == 1) {
assoc_desc = udev->actconfig->intf_assoc[0];
if (assoc_desc->bFirstInterface != ifnum) {
cx231xx_err(DRIVER_NAME ": Not found "
"matching IAD interface\n");
return -ENODEV;
}
}
}
if (skip_interface)
return -ENODEV;
cx231xx_info("registering interface %d\n", ifnum);
/* save our data pointer in this interface device */
usb_set_intfdata(lif, dev);
if ((dev->interface_count - 1) != dev->max_iad_interface_count)
return 0;
/*
* AV device initialization - only done at the last interface
*/
/* Create v4l2 device */
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
cx231xx_errdev("v4l2_device_register failed\n");
cx231xx_devused &= ~(1 << nr);
kfree(dev);
return -EIO;
}
/* allocate device struct */
retval = cx231xx_init_dev(&dev, udev, nr);
if (retval) {
cx231xx_devused &= ~(1 << dev->devno);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return retval;
}
/* compute alternate max packet sizes for video */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.video_index + 1];
dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0].
endpoint[isoc_pipe].desc.bEndpointAddress);
dev->video_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->video_mode.end_point_addr,
dev->video_mode.num_alt);
dev->video_mode.alt_max_pkt_size =
kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL);
if (dev->video_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->video_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->video_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->video_mode.alt_max_pkt_size[i]);
}
/* compute alternate max packet sizes for vbi */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
vanc_index + 1];
dev->vbi_mode.end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress);
dev->vbi_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->vbi_mode.end_point_addr,
dev->vbi_mode.num_alt);
dev->vbi_mode.alt_max_pkt_size =
kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL);
if (dev->vbi_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->vbi_mode.num_alt; i++) {
u16 tmp =
le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->vbi_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->vbi_mode.alt_max_pkt_size[i]);
}
/* compute alternate max packet sizes for sliced CC */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].interface_info.
hanc_index + 1];
dev->sliced_cc_mode.end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress);
dev->sliced_cc_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->sliced_cc_mode.end_point_addr,
dev->sliced_cc_mode.num_alt);
dev->sliced_cc_mode.alt_max_pkt_size =
kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL);
if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->sliced_cc_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->sliced_cc_mode.alt_max_pkt_size[i]);
}
if (dev->current_pcb_config.ts1_source != 0xff) {
/* compute alternate max packet sizes for TS1 */
uif = udev->actconfig->interface[dev->current_pcb_config.
hs_config_info[0].
interface_info.
ts1_index + 1];
dev->ts1_mode.end_point_addr =
le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].
desc.bEndpointAddress);
dev->ts1_mode.num_alt = uif->num_altsetting;
cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->ts1_mode.end_point_addr,
dev->ts1_mode.num_alt);
dev->ts1_mode.alt_max_pkt_size =
kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL);
if (dev->ts1_mode.alt_max_pkt_size == NULL) {
cx231xx_errdev("out of memory!\n");
cx231xx_devused &= ~(1 << nr);
v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return -ENOMEM;
}
for (i = 0; i < dev->ts1_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].
endpoint[isoc_pipe].desc.
wMaxPacketSize);
dev->ts1_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
cx231xx_info("Alternate setting %i, max size= %i\n", i,
dev->ts1_mode.alt_max_pkt_size[i]);
}
}
/* load other modules required */
request_modules(dev);
return 0;
}
/*
* cx231xx_usb_disconnect()
* called when the device gets diconencted
* video device will be unregistered on v4l2_close in case it is still open
*/
static void cx231xx_usb_disconnect(struct usb_interface *interface)
{
struct cx231xx *dev;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (!dev)
return;
if (!dev->udev)
return;
/* delete v4l2 device */
v4l2_device_unregister(&dev->v4l2_dev);
/* wait until all current v4l2 io is finished then deallocate
resources */
mutex_lock(&dev->lock);
wake_up_interruptible_all(&dev->open);
if (dev->users) {
cx231xx_warn
("device /dev/video%d is open! Deregistration and memory "
"deallocation are deferred on close.\n", dev->vdev->num);
dev->state |= DEV_MISCONFIGURED;
cx231xx_uninit_isoc(dev);
dev->state |= DEV_DISCONNECTED;
wake_up_interruptible(&dev->wait_frame);
wake_up_interruptible(&dev->wait_stream);
} else {
dev->state |= DEV_DISCONNECTED;
cx231xx_release_resources(dev);
}
cx231xx_close_extension(dev);
mutex_unlock(&dev->lock);
if (!dev->users) {
kfree(dev->video_mode.alt_max_pkt_size);
kfree(dev->vbi_mode.alt_max_pkt_size);
kfree(dev->sliced_cc_mode.alt_max_pkt_size);
kfree(dev->ts1_mode.alt_max_pkt_size);
kfree(dev);
}
}
static struct usb_driver cx231xx_usb_driver = {
.name = "cx231xx",
.probe = cx231xx_usb_probe,
.disconnect = cx231xx_usb_disconnect,
.id_table = cx231xx_id_table,
};
static int __init cx231xx_module_init(void)
{
int result;
printk(KERN_INFO DRIVER_NAME " v4l2 driver loaded.\n");
/* register this driver with the USB subsystem */
result = usb_register(&cx231xx_usb_driver);
if (result)
cx231xx_err(DRIVER_NAME
" usb_register failed. Error number %d.\n", result);
return result;
}
static void __exit cx231xx_module_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&cx231xx_usb_driver);
}
module_init(cx231xx_module_init);
module_exit(cx231xx_module_exit);

View File

@ -0,0 +1,494 @@
/*
cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB
video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
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.
*/
#ifndef _POLARIS_REG_H_
#define _POLARIS_REG_H_
#define BOARD_CFG_STAT 0x0
#define TS_MODE_REG 0x4
#define TS1_CFG_REG 0x8
#define TS1_LENGTH_REG 0xc
#define TS2_CFG_REG 0x10
#define TS2_LENGTH_REG 0x14
#define EP_MODE_SET 0x18
#define CIR_PWR_PTN1 0x1c
#define CIR_PWR_PTN2 0x20
#define CIR_PWR_PTN3 0x24
#define CIR_PWR_MASK0 0x28
#define CIR_PWR_MASK1 0x2c
#define CIR_PWR_MASK2 0x30
#define CIR_GAIN 0x34
#define CIR_CAR_REG 0x38
#define CIR_OT_CFG1 0x40
#define CIR_OT_CFG2 0x44
#define PWR_CTL_EN 0x74
/* Polaris Endpoints capture mask for register EP_MODE_SET */
#define ENABLE_EP1 0x01 /* Bit[0]=1 */
#define ENABLE_EP2 0x02 /* Bit[1]=1 */
#define ENABLE_EP3 0x04 /* Bit[2]=1 */
#define ENABLE_EP4 0x08 /* Bit[3]=1 */
#define ENABLE_EP5 0x10 /* Bit[4]=1 */
#define ENABLE_EP6 0x20 /* Bit[5]=1 */
/* Bit definition for register PWR_CTL_EN */
#define PWR_MODE_MASK 0x17f
#define PWR_AV_EN 0x08 /* bit3 */
#define PWR_ISO_EN 0x40 /* bit6 */
#define PWR_AV_MODE 0x30 /* bit4,5 */
#define PWR_TUNER_EN 0x04 /* bit2 */
#define PWR_DEMOD_EN 0x02 /* bit1 */
#define I2C_DEMOD_EN 0x01 /* bit0 */
#define PWR_RESETOUT_EN 0x100 /* bit8 */
enum AV_MODE{
POLARIS_AVMODE_DEFAULT = 0,
POLARIS_AVMODE_DIGITAL = 0x10,
POLARIS_AVMODE_ANALOGT_TV = 0x20,
POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
};
/* Colibri Registers */
#define SINGLE_ENDED 0x0
#define LOW_IF 0x4
#define EU_IF 0x9
#define US_IF 0xa
#define SUP_BLK_TUNE1 0x00
#define SUP_BLK_TUNE2 0x01
#define SUP_BLK_TUNE3 0x02
#define SUP_BLK_XTAL 0x03
#define SUP_BLK_PLL1 0x04
#define SUP_BLK_PLL2 0x05
#define SUP_BLK_PLL3 0x06
#define SUP_BLK_REF 0x07
#define SUP_BLK_PWRDN 0x08
#define SUP_BLK_TESTPAD 0x09
#define ADC_COM_INT5_STAB_REF 0x0a
#define ADC_COM_QUANT 0x0b
#define ADC_COM_BIAS1 0x0c
#define ADC_COM_BIAS2 0x0d
#define ADC_COM_BIAS3 0x0e
#define TESTBUS_CTRL 0x12
#define FLD_PWRDN_TUNING_BIAS 0x10
#define FLD_PWRDN_ENABLE_PLL 0x08
#define FLD_PWRDN_PD_BANDGAP 0x04
#define FLD_PWRDN_PD_BIAS 0x02
#define FLD_PWRDN_PD_TUNECK 0x01
#define ADC_STATUS_CH1 0x20
#define ADC_STATUS_CH2 0x40
#define ADC_STATUS_CH3 0x60
#define ADC_STATUS2_CH1 0x21
#define ADC_STATUS2_CH2 0x41
#define ADC_STATUS2_CH3 0x61
#define ADC_CAL_ATEST_CH1 0x22
#define ADC_CAL_ATEST_CH2 0x42
#define ADC_CAL_ATEST_CH3 0x62
#define ADC_PWRDN_CLAMP_CH1 0x23
#define ADC_PWRDN_CLAMP_CH2 0x43
#define ADC_PWRDN_CLAMP_CH3 0x63
#define ADC_CTRL_DAC23_CH1 0x24
#define ADC_CTRL_DAC23_CH2 0x44
#define ADC_CTRL_DAC23_CH3 0x64
#define ADC_CTRL_DAC1_CH1 0x25
#define ADC_CTRL_DAC1_CH2 0x45
#define ADC_CTRL_DAC1_CH3 0x65
#define ADC_DCSERVO_DEM_CH1 0x26
#define ADC_DCSERVO_DEM_CH2 0x46
#define ADC_DCSERVO_DEM_CH3 0x66
#define ADC_FB_FRCRST_CH1 0x27
#define ADC_FB_FRCRST_CH2 0x47
#define ADC_FB_FRCRST_CH3 0x67
#define ADC_INPUT_CH1 0x28
#define ADC_INPUT_CH2 0x48
#define ADC_INPUT_CH3 0x68
#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */
#define ADC_NTF_PRECLMP_EN_CH1 0x29
#define ADC_NTF_PRECLMP_EN_CH2 0x49
#define ADC_NTF_PRECLMP_EN_CH3 0x69
#define ADC_QGAIN_RES_TRM_CH1 0x2a
#define ADC_QGAIN_RES_TRM_CH2 0x4a
#define ADC_QGAIN_RES_TRM_CH3 0x6a
#define ADC_SOC_PRECLMP_TERM_CH1 0x2b
#define ADC_SOC_PRECLMP_TERM_CH2 0x4b
#define ADC_SOC_PRECLMP_TERM_CH3 0x6b
#define TESTBUS_CTRL_CH1 0x32
#define TESTBUS_CTRL_CH2 0x52
#define TESTBUS_CTRL_CH3 0x72
/******************************************************************************
* DIF registers *
******************************************************************************/
#define DIRECT_IF_REVB_BASE 0x00300
/*****************************************************************************/
#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000)
/*****************************************************************************/
#define FLD_DIF_PLL_LOCK 0x80000000
/* Reserved [30:29] */
#define FLD_DIF_PLL_FREE_RUN 0x10000000
#define FLD_DIF_PLL_FREQ 0x0fffffff
/*****************************************************************************/
#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004)
/*****************************************************************************/
#define FLD_DIF_KD_PD 0xff000000
/* Reserved [23:20] */
#define FLD_DIF_KDS_PD 0x000f0000
#define FLD_DIF_KI_PD 0x0000ff00
/* Reserved [7:4] */
#define FLD_DIF_KIS_PD 0x0000000f
/*****************************************************************************/
#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008)
/*****************************************************************************/
#define FLD_DIF_KD_FD 0xff000000
/* Reserved [23:20] */
#define FLD_DIF_KDS_FD 0x000f0000
#define FLD_DIF_KI_FD 0x0000ff00
#define FLD_DIF_SIG_PROP_SZ 0x000000f0
#define FLD_DIF_KIS_FD 0x0000000f
/*****************************************************************************/
#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c)
/*****************************************************************************/
#define FLD_DIF_PLL_AGC_REF 0xfff00000
#define FLD_DIF_PLL_AGC_KI 0x000f0000
/* Reserved [15] */
#define FLD_DIF_FREQ_LIMIT 0x00007000
#define FLD_DIF_K_FD 0x00000f00
#define FLD_DIF_DOWNSMPL_FD 0x000000ff
/*****************************************************************************/
#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_PLL_AGC_EN 0x00008000
/* Reserved [14:12] */
#define FLD_DIF_PLL_MAN_GAIN 0x00000fff
/*****************************************************************************/
#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014)
/*****************************************************************************/
#define FLD_DIF_K_AGC_RF 0xf0000000
#define FLD_DIF_K_AGC_IF 0x0f000000
#define FLD_DIF_K_AGC_INT 0x00f00000
/* Reserved [19:12] */
#define FLD_DIF_IF_REF 0x00000fff
/*****************************************************************************/
#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018)
/*****************************************************************************/
#define FLD_DIF_IF_MAX 0xff000000
#define FLD_DIF_IF_MIN 0x00ff0000
#define FLD_DIF_IF_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c)
/*****************************************************************************/
#define FLD_DIF_INT_MAX 0xff000000
#define FLD_DIF_INT_MIN 0x00ff0000
#define FLD_DIF_INT_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020)
/*****************************************************************************/
#define FLD_DIF_RF_MAX 0xff000000
#define FLD_DIF_RF_MIN 0x00ff0000
#define FLD_DIF_RF_AGC 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024)
/*****************************************************************************/
#define FLD_DIF_IF_AGC_IN 0xffff0000
#define FLD_DIF_INT_AGC_IN 0x0000ffff
/*****************************************************************************/
#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_RF_AGC_IN 0x0000ffff
/*****************************************************************************/
#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c)
/*****************************************************************************/
#define FLD_DIF_AFD 0xc0000000
#define FLD_DIF_K_VID_AGC 0x30000000
#define FLD_DIF_LINE_LENGTH 0x0fff0000
#define FLD_DIF_AGC_GAIN 0x0000ffff
/*****************************************************************************/
#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030)
/*****************************************************************************/
#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000
/* Reserved [30:30] */
#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000
/* Reserved [23:17] */
#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000
#define FLD_DIF_VID_MAN_GAIN 0x0000ffff
/*****************************************************************************/
#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034)
/*****************************************************************************/
#define FLD_DIF_LPF_FREQ 0xc0000000
#define FLD_DIF_AV_PHASE_INC 0x3f000000
#define FLD_DIF_AUDIO_FREQ 0x00ffffff
/*****************************************************************************/
#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038)
/*****************************************************************************/
/* Reserved [31:24] */
#define FLD_DIF_IIR23_R2 0x00ff0000
#define FLD_DIF_IIR23_R1 0x0000ff00
#define FLD_DIF_IIR1_R1 0x000000ff
/*****************************************************************************/
#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c)
/*****************************************************************************/
#define FLD_DIF_DIF_BYPASS 0x80000000
#define FLD_DIF_FM_NYQ_GAIN 0x40000000
#define FLD_DIF_RF_AGC_ENA 0x20000000
#define FLD_DIF_INT_AGC_ENA 0x10000000
#define FLD_DIF_IF_AGC_ENA 0x08000000
#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000
#define FLD_DIF_VIDEO_AGC_ENA 0x02000000
#define FLD_DIF_RF_AGC_INV 0x01000000
#define FLD_DIF_INT_AGC_INV 0x00800000
#define FLD_DIF_IF_AGC_INV 0x00400000
#define FLD_DIF_SPEC_INV 0x00200000
#define FLD_DIF_AUD_FULL_BW 0x00100000
#define FLD_DIF_AUD_SRC_SEL 0x00080000
/* Reserved [18] */
#define FLD_DIF_IF_FREQ 0x00030000
/* Reserved [15:14] */
#define FLD_DIF_TIP_OFFSET 0x00003f00
/* Reserved [7:5] */
#define FLD_DIF_DITHER_ENA 0x00000010
/* Reserved [3:1] */
#define FLD_DIF_RF_IF_LOCK 0x00000001
/*****************************************************************************/
#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_PHASE_INC 0x1fffffff
/*****************************************************************************/
#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044)
/*****************************************************************************/
/* Reserved [31:16] */
#define FLD_DIF_SRC_KI 0x0000ff00
#define FLD_DIF_SRC_KD 0x000000ff
/*****************************************************************************/
#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048)
/*****************************************************************************/
/* Reserved [31:19] */
#define FLD_DIF_BPF_COEFF_0 0x00070000
/* Reserved [15:4] */
#define FLD_DIF_BPF_COEFF_1 0x0000000f
/*****************************************************************************/
#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c)
/*****************************************************************************/
/* Reserved [31:22] */
#define FLD_DIF_BPF_COEFF_2 0x003f0000
/* Reserved [15:7] */
#define FLD_DIF_BPF_COEFF_3 0x0000007f
/*****************************************************************************/
#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050)
/*****************************************************************************/
/* Reserved [31:24] */
#define FLD_DIF_BPF_COEFF_4 0x00ff0000
/* Reserved [15:8] */
#define FLD_DIF_BPF_COEFF_5 0x000000ff
/*****************************************************************************/
#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054)
/*****************************************************************************/
/* Reserved [31:25] */
#define FLD_DIF_BPF_COEFF_6 0x01ff0000
/* Reserved [15:9] */
#define FLD_DIF_BPF_COEFF_7 0x000001ff
/*****************************************************************************/
#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058)
/*****************************************************************************/
/* Reserved [31:26] */
#define FLD_DIF_BPF_COEFF_8 0x03ff0000
/* Reserved [15:10] */
#define FLD_DIF_BPF_COEFF_9 0x000003ff
/*****************************************************************************/
#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c)
/*****************************************************************************/
/* Reserved [31:27] */
#define FLD_DIF_BPF_COEFF_10 0x07ff0000
/* Reserved [15:11] */
#define FLD_DIF_BPF_COEFF_11 0x000007ff
/*****************************************************************************/
#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060)
/*****************************************************************************/
/* Reserved [31:27] */
#define FLD_DIF_BPF_COEFF_12 0x07ff0000
/* Reserved [15:12] */
#define FLD_DIF_BPF_COEFF_13 0x00000fff
/*****************************************************************************/
#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064)
/*****************************************************************************/
/* Reserved [31:28] */
#define FLD_DIF_BPF_COEFF_14 0x0fff0000
/* Reserved [15:12] */
#define FLD_DIF_BPF_COEFF_15 0x00000fff
/*****************************************************************************/
#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_16 0x1fff0000
/* Reserved [15:13] */
#define FLD_DIF_BPF_COEFF_17 0x00001fff
/*****************************************************************************/
#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_18 0x1fff0000
/* Reserved [15:13] */
#define FLD_DIF_BPF_COEFF_19 0x00001fff
/*****************************************************************************/
#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070)
/*****************************************************************************/
/* Reserved [31:29] */
#define FLD_DIF_BPF_COEFF_20 0x1fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_21 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_22 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_23 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_24 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_25 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_26 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_27 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_28 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_29 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_30 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_31 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_32 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_33 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_34 0x3fff0000
/* Reserved [15:14] */
#define FLD_DIF_BPF_COEFF_35 0x00003fff
/*****************************************************************************/
#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090)
/*****************************************************************************/
/* Reserved [31:30] */
#define FLD_DIF_BPF_COEFF_36 0x3fff0000
/* Reserved [15:0] */
/*****************************************************************************/
#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094)
/*****************************************************************************/
/* Reserved [31:20] */
#define FLD_DIF_RPT_VARIANCE 0x000fffff
/*****************************************************************************/
#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098)
/*****************************************************************************/
/* Reserved [31:8] */
#define FLD_DIF_DIF_SOFT_RST 0x00000080
#define FLD_DIF_DIF_REG_RST_MSK 0x00000040
#define FLD_DIF_AGC_RST_MSK 0x00000020
#define FLD_DIF_CMP_RST_MSK 0x00000010
#define FLD_DIF_AVS_RST_MSK 0x00000008
#define FLD_DIF_NYQ_RST_MSK 0x00000004
#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002
#define FLD_DIF_PLL_RST_MSK 0x00000001
/*****************************************************************************/
#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c)
/*****************************************************************************/
/* Reserved [31:25] */
#define FLD_DIF_CTL_IP 0x01ffffff
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,559 @@
/*
DVB device driver for cx231xx
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
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/usb.h>
#include "cx231xx.h"
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
#include "xc5000.h"
#include "dvb_dummy_fe.h"
MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
MODULE_LICENSE("GPL");
static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define dprintk(level, fmt, arg...) do { \
if (debug >= level) \
printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
} while (0)
#define CX231XX_DVB_NUM_BUFS 5
#define CX231XX_DVB_MAX_PACKETSIZE 564
#define CX231XX_DVB_MAX_PACKETS 64
struct cx231xx_dvb {
struct dvb_frontend *frontend;
/* feed count management */
struct mutex lock;
int nfeeds;
/* general boilerplate stuff */
struct dvb_adapter adapter;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
};
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
switch (status) {
case -ENOENT:
errmsg = "unlinked synchronuously";
break;
case -ECONNRESET:
errmsg = "unlinked asynchronuously";
break;
case -ENOSR:
errmsg = "Buffer error (overrun)";
break;
case -EPIPE:
errmsg = "Stalled (device not responding)";
break;
case -EOVERFLOW:
errmsg = "Babble (bad cable?)";
break;
case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)";
break;
case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)";
break;
case -ETIME:
errmsg = "Device does not respond";
break;
}
if (packet < 0) {
dprintk(1, "URB status %d [%s].\n", status, errmsg);
} else {
dprintk(1, "URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
{
int i;
if (!dev)
return 0;
if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
for (i = 0; i < urb->number_of_packets; i++) {
int status = urb->iso_frame_desc[i].status;
if (status < 0) {
print_err_status(dev, i, status);
if (urb->iso_frame_desc[i].status != -EPROTO)
continue;
}
dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
}
return 0;
}
static int start_streaming(struct cx231xx_dvb *dvb)
{
int rc;
struct cx231xx *dev = dvb->adapter.priv;
usb_set_interface(dev->udev, 0, 1);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
return rc;
return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
CX231XX_DVB_NUM_BUFS,
CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy);
}
static int stop_streaming(struct cx231xx_dvb *dvb)
{
struct cx231xx *dev = dvb->adapter.priv;
cx231xx_uninit_isoc(dev);
cx231xx_set_mode(dev, CX231XX_SUSPEND);
return 0;
}
static int start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx231xx_dvb *dvb = demux->priv;
int rc, ret;
if (!demux->dmx.frontend)
return -EINVAL;
mutex_lock(&dvb->lock);
dvb->nfeeds++;
rc = dvb->nfeeds;
if (dvb->nfeeds == 1) {
ret = start_streaming(dvb);
if (ret < 0)
rc = ret;
}
mutex_unlock(&dvb->lock);
return rc;
}
static int stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx231xx_dvb *dvb = demux->priv;
int err = 0;
mutex_lock(&dvb->lock);
dvb->nfeeds--;
if (0 == dvb->nfeeds)
err = stop_streaming(dvb);
mutex_unlock(&dvb->lock);
return err;
}
/* ------------------------------------------------------------------ */
static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
struct cx231xx *dev = fe->dvb->priv;
if (acquire)
return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
else
return cx231xx_set_mode(dev, CX231XX_SUSPEND);
}
/* ------------------------------------------------------------------ */
static struct xc5000_config cnxt_rde250_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 5380,
};
/* ------------------------------------------------------------------ */
#if 0
static int attach_xc5000(u8 addr, struct cx231xx *dev)
{
struct dvb_frontend *fe;
struct xc5000_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap;
cfg.i2c_addr = addr;
if (!dev->dvb->frontend) {
printk(KERN_ERR "%s/2: dvb frontend not attached. "
"Can't attach xc5000\n", dev->name);
return -EINVAL;
}
fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
if (!fe) {
printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
dvb_frontend_detach(dev->dvb->frontend);
dev->dvb->frontend = NULL;
return -EINVAL;
}
printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
return 0;
}
#endif
int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
{
int status = 0;
if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
if (dops->set_analog_params != NULL) {
struct analog_parameters params;
params.frequency = freq;
params.std = dev->norm;
params.mode = 0; /* 0- Air; 1 - cable */
/*params.audmode = ; */
/* Set the analog parameters to set the frequency */
cx231xx_info("Setting Frequency for XC5000\n");
dops->set_analog_params(dev->dvb->frontend, &params);
}
}
return status;
}
int cx231xx_reset_analog_tuner(struct cx231xx *dev)
{
int status = 0;
if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
if (dops->init != NULL && !dev->xc_fw_load_done) {
cx231xx_info("Reloading firmware for XC5000\n");
status = dops->init(dev->dvb->frontend);
if (status == 0) {
dev->xc_fw_load_done = 1;
cx231xx_info
("XC5000 firmware download completed\n");
} else {
dev->xc_fw_load_done = 0;
cx231xx_info
("XC5000 firmware download failed !!!\n");
}
}
}
return status;
}
/* ------------------------------------------------------------------ */
static int register_dvb(struct cx231xx_dvb *dvb,
struct module *module,
struct cx231xx *dev, struct device *device)
{
int result;
mutex_init(&dvb->lock);
/* register adapter */
result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
adapter_nr);
if (result < 0) {
printk(KERN_WARNING
"%s: dvb_register_adapter failed (errno = %d)\n",
dev->name, result);
goto fail_adapter;
}
/* Ensure all frontends negotiate bus access */
dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
dvb->adapter.priv = dev;
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
printk(KERN_WARNING
"%s: dvb_register_frontend failed (errno = %d)\n",
dev->name, result);
goto fail_frontend;
}
/* register demux stuff */
dvb->demux.dmx.capabilities =
DMX_TS_FILTERING | DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING;
dvb->demux.priv = dvb;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
dvb->demux.start_feed = start_feed;
dvb->demux.stop_feed = stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmx;
}
dvb->dmxdev.filternum = 256;
dvb->dmxdev.demux = &dvb->demux.dmx;
dvb->dmxdev.capabilities = 0;
result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (result < 0) {
printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmxdev;
}
dvb->fe_hw.source = DMX_FRONTEND_0;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_WARNING
"%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
dev->name, result);
goto fail_fe_hw;
}
dvb->fe_mem.source = DMX_MEMORY_FE;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
if (result < 0) {
printk(KERN_WARNING
"%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
dev->name, result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_WARNING
"%s: connect_frontend failed (errno = %d)\n", dev->name,
result);
goto fail_fe_conn;
}
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
return 0;
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
fail_fe_hw:
dvb_dmxdev_release(&dvb->dmxdev);
fail_dmxdev:
dvb_dmx_release(&dvb->demux);
fail_dmx:
dvb_unregister_frontend(dvb->frontend);
fail_frontend:
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
return result;
}
static void unregister_dvb(struct cx231xx_dvb *dvb)
{
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
dvb_unregister_frontend(dvb->frontend);
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
}
static int dvb_init(struct cx231xx *dev)
{
int result = 0;
struct cx231xx_dvb *dvb;
if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
if (dvb == NULL) {
printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
return -ENOMEM;
}
dev->dvb = dvb;
dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
/* init frontend */
switch (dev->model) {
case CX231XX_BOARD_CNXT_RDE_250:
/* dev->dvb->frontend = dvb_attach(s5h1411_attach,
&dvico_s5h1411_config,
&dev->i2c_bus[1].i2c_adap); */
dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach dummy front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[1].i2c_adap,
&cnxt_rde250_tunerconfig) < 0) {
result = -EINVAL;
goto out_free;
}
break;
case CX231XX_BOARD_CNXT_RDU_250:
dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
if (dev->dvb->frontend == NULL) {
printk(DRIVER_NAME
": Failed to attach dummy front end\n");
result = -EINVAL;
goto out_free;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = cx231xx_tuner_callback;
if (dvb_attach(xc5000_attach, dev->dvb->frontend,
&dev->i2c_bus[1].i2c_adap,
&cnxt_rde250_tunerconfig) < 0) {
result = -EINVAL;
goto out_free;
}
break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n", dev->name);
break;
}
if (NULL == dvb->frontend) {
printk(KERN_ERR
"%s/2: frontend initialization failed\n", dev->name);
result = -EINVAL;
goto out_free;
}
/* register everything */
result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
if (result < 0)
goto out_free;
cx231xx_set_mode(dev, CX231XX_SUSPEND);
printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
return 0;
out_free:
cx231xx_set_mode(dev, CX231XX_SUSPEND);
kfree(dvb);
dev->dvb = NULL;
return result;
}
static int dvb_fini(struct cx231xx *dev)
{
if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
if (dev->dvb) {
unregister_dvb(dev->dvb);
dev->dvb = NULL;
}
return 0;
}
static struct cx231xx_ops dvb_ops = {
.id = CX231XX_DVB,
.name = "Cx231xx dvb Extension",
.init = dvb_init,
.fini = dvb_fini,
};
static int __init cx231xx_dvb_register(void)
{
return cx231xx_register_extension(&dvb_ops);
}
static void __exit cx231xx_dvb_unregister(void)
{
cx231xx_unregister_extension(&dvb_ops);
}
module_init(cx231xx_dvb_register);
module_exit(cx231xx_dvb_unregister);

View File

@ -0,0 +1,555 @@
/*
cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
Based on Cx23885 driver
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/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include "cx231xx.h"
/* ----------------------------------------------------------- */
static unsigned int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
#define dprintk1(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
printk(fmt, ##args); \
} \
} while (0)
#define dprintk2(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
printk(KERN_DEBUG "%s at %s: " fmt, \
dev->name, __func__ , ##args); \
} \
} while (0)
/*
* cx231xx_i2c_send_bytes()
*/
int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 size = 0;
u8 loop = 0;
u8 saddr_len = 1;
u8 *buf_ptr = NULL;
u16 saddr = 0;
u8 need_gpio = 0;
if ((bus->nr == 1) && (msg->addr == 0x61)
&& (dev->tuner_type == TUNER_XC5000)) {
size = msg->len;
if (size == 2) { /* register write sub addr */
/* Just writing sub address will cause problem
* to XC5000. So ignore the request */
return 0;
} else if (size == 4) { /* register write with sub addr */
if (msg->len >= 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
saddr = msg->buf[0];
switch (saddr) {
case 0x0000: /* start tuner calibration mode */
need_gpio = 1;
/* FW Loading is done */
dev->xc_fw_load_done = 1;
break;
case 0x000D: /* Set signal source */
case 0x0001: /* Set TV standard - Video */
case 0x0002: /* Set TV standard - Audio */
case 0x0003: /* Set RF Frequency */
need_gpio = 1;
break;
default:
if (dev->xc_fw_load_done)
need_gpio = 1;
break;
}
if (need_gpio) {
dprintk1(1,
"GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n",
msg->addr, msg->len, saddr);
return dev->cx231xx_gpio_i2c_write(dev,
msg->addr,
msg->buf,
msg->len);
}
}
/* special case for Xc5000 tuner case */
saddr_len = 1;
/* adjust the length to correct length */
size -= saddr_len;
buf_ptr = (u8 *) (msg->buf + 1);
do {
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = saddr_len;
req_data.saddr_dat = msg->buf[0];
req_data.buf_size = size > 16 ? 16 : size;
req_data.p_buffer = (u8 *) (buf_ptr + loop * 16);
bus->i2c_nostop = (size > 16) ? 1 : 0;
bus->i2c_reserve = (loop == 0) ? 0 : 1;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
loop++;
if (size >= 16)
size -= 16;
else
size = 0;
} while (size > 0);
bus->i2c_nostop = 0;
bus->i2c_reserve = 0;
} else { /* regular case */
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
}
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_recv_bytes()
* read a byte from the i2c device
*/
static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 saddr = 0;
u8 need_gpio = 0;
if ((bus->nr == 1) && (msg->addr == 0x61)
&& dev->tuner_type == TUNER_XC5000) {
if (msg->len == 2)
saddr = msg->buf[0] << 8 | msg->buf[1];
else if (msg->len == 1)
saddr = msg->buf[0];
if (dev->xc_fw_load_done) {
switch (saddr) {
case 0x0009: /* BUSY check */
dprintk1(1,
"GPIO R E A D: Special case BUSY check \n");
/*Try read BUSY register, just set it to zero*/
msg->buf[0] = 0;
if (msg->len == 2)
msg->buf[1] = 0;
return 0;
case 0x0004: /* read Lock status */
need_gpio = 1;
break;
}
if (need_gpio) {
/* this is a special case to handle Xceive tuner
clock stretch issue with gpio based I2C */
dprintk1(1,
"GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n",
msg->addr, msg->len,
msg->buf[0] << 8 | msg->buf[1]);
status =
dev->cx231xx_gpio_i2c_write(dev, msg->addr,
msg->buf,
msg->len);
status =
dev->cx231xx_gpio_i2c_read(dev, msg->addr,
msg->buf,
msg->len);
return status;
}
}
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = msg->len;
req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1];
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
} else {
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = msg->len;
req_data.p_buffer = msg->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
}
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_recv_bytes_with_saddr()
* read a byte from the i2c device
*/
static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg1,
const struct i2c_msg *msg2)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
u16 saddr = 0;
u8 need_gpio = 0;
if (msg1->len == 2)
saddr = msg1->buf[0] << 8 | msg1->buf[1];
else if (msg1->len == 1)
saddr = msg1->buf[0];
if ((bus->nr == 1) && (msg2->addr == 0x61)
&& dev->tuner_type == TUNER_XC5000) {
if ((msg2->len < 16)) {
dprintk1(1,
"i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n",
msg2->addr, msg2->len, saddr, msg1->len);
switch (saddr) {
case 0x0008: /* read FW load status */
need_gpio = 1;
break;
case 0x0004: /* read Lock status */
need_gpio = 1;
break;
}
if (need_gpio) {
status =
dev->cx231xx_gpio_i2c_write(dev, msg1->addr,
msg1->buf,
msg1->len);
status =
dev->cx231xx_gpio_i2c_read(dev, msg2->addr,
msg2->buf,
msg2->len);
return status;
}
}
}
/* prepare xfer_data struct */
req_data.dev_addr = msg2->addr;
req_data.direction = msg2->flags;
req_data.saddr_len = msg1->len;
req_data.saddr_dat = saddr;
req_data.buf_size = msg2->len;
req_data.p_buffer = msg2->buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_check_for_device()
* check if there is a i2c_device at the supplied address
*/
static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
req_data.direction = msg->flags;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
req_data.buf_size = 0;
req_data.p_buffer = NULL;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
return status < 0 ? status : 0;
}
/*
* cx231xx_i2c_xfer()
* the main i2c transfer function
*/
static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
int addr, rc, i, byte;
if (num <= 0)
return 0;
for (i = 0; i < num; i++) {
addr = msgs[i].addr >> 1;
dprintk2(2, "%s %s addr=%x len=%d:",
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
if (!msgs[i].len) {
/* no len: check only for device presence */
rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
if (rc < 0) {
dprintk2(2, " no device\n");
return rc;
}
} else if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]);
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
msgs[i].addr == msgs[i + 1].addr
&& (msgs[i].len <= 2) && (bus->nr < 2)) {
/* read bytes */
rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
&msgs[i],
&msgs[i + 1]);
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
i++;
} else {
/* write bytes */
if (i2c_debug >= 2) {
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]);
}
if (rc < 0)
goto err;
if (i2c_debug >= 2)
printk("\n");
}
return num;
err:
dprintk2(2, " ERROR: %i\n", rc);
return rc;
}
/* ----------------------------------------------------------- */
/*
* functionality()
*/
static u32 functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
/*
* attach_inform()
* gets called when a device attaches to the i2c bus
* does some basic configuration
*/
static int attach_inform(struct i2c_client *client)
{
struct cx231xx_i2c *bus = i2c_get_adapdata(client->adapter);
struct cx231xx *dev = bus->dev;
switch (client->addr << 1) {
case 0x8e:
{
struct IR_i2c *ir = i2c_get_clientdata(client);
dprintk1(1, "attach_inform: IR detected (%s).\n",
ir->phys);
cx231xx_set_ir(dev, ir);
break;
}
break;
default:
break;
}
return 0;
}
static struct i2c_algorithm cx231xx_algo = {
.master_xfer = cx231xx_i2c_xfer,
.functionality = functionality,
};
static struct i2c_adapter cx231xx_adap_template = {
.owner = THIS_MODULE,
.name = "cx231xx",
.id = I2C_HW_B_CX231XX,
.algo = &cx231xx_algo,
.client_register = attach_inform,
};
static struct i2c_client cx231xx_client_template = {
.name = "cx231xx internal",
};
/* ----------------------------------------------------------- */
/*
* i2c_devs
* incomplete list of known devices
*/
static char *i2c_devs[128] = {
[0x60 >> 1] = "colibri",
[0x88 >> 1] = "hammerhead",
[0x8e >> 1] = "CIR",
[0x32 >> 1] = "GeminiIII",
[0x02 >> 1] = "Aquarius",
[0xa0 >> 1] = "eeprom",
[0xc0 >> 1] = "tuner/XC3028",
[0xc2 >> 1] = "tuner/XC5000",
};
/*
* cx231xx_do_i2c_scan()
* check i2c address range for devices
*/
void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
{
unsigned char buf;
int i, rc;
cx231xx_info(": Checking for I2C devices ..\n");
for (i = 0; i < 128; i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 0);
if (rc < 0)
continue;
cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
dev->name, i << 1,
i2c_devs[i] ? i2c_devs[i] : "???");
}
cx231xx_info(": Completed Checking for I2C devices.\n");
}
/*
* cx231xx_i2c_register()
* register i2c bus
*/
int cx231xx_i2c_register(struct cx231xx_i2c *bus)
{
struct cx231xx *dev = bus->dev;
BUG_ON(!dev->cx231xx_send_usb_command);
memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
memcpy(&bus->i2c_client, &cx231xx_client_template,
sizeof(bus->i2c_client));
bus->i2c_adap.dev.parent = &dev->udev->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
if (0 == bus->i2c_rc) {
if (i2c_scan)
cx231xx_do_i2c_scan(dev, &bus->i2c_client);
} else
cx231xx_warn("%s: i2c bus %d register FAILED\n",
dev->name, bus->nr);
return bus->i2c_rc;
}
/*
* cx231xx_i2c_unregister()
* unregister i2c_bus
*/
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
{
i2c_del_adapter(&bus->i2c_adap);
return 0;
}

View File

@ -0,0 +1,246 @@
/*
handle cx231xx IR remotes via linux kernel input layer.
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
< This is a place holder for IR now.>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/usb.h>
#include "cx231xx.h"
static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
#define i2cdprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
}
#define dprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
}
/**********************************************************
Polling structure used by cx231xx IR's
**********************************************************/
struct cx231xx_ir_poll_result {
unsigned int toggle_bit:1;
unsigned int read_count:7;
u8 rc_address;
u8 rc_data[4];
};
struct cx231xx_IR {
struct cx231xx *dev;
struct input_dev *input;
struct ir_input_state ir;
char name[32];
char phys[32];
/* poll external decoder */
int polling;
struct work_struct work;
struct timer_list timer;
unsigned int last_toggle:1;
unsigned int last_readcount;
unsigned int repeat_interval;
int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *);
};
/**********************************************************
Polling code for cx231xx
**********************************************************/
static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
{
int result;
int do_sendkey = 0;
struct cx231xx_ir_poll_result poll_result;
/* read the registers containing the IR status */
result = ir->get_key(ir, &poll_result);
if (result < 0) {
dprintk("ir->get_key() failed %d\n", result);
return;
}
dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
poll_result.toggle_bit, poll_result.read_count,
ir->last_readcount, poll_result.rc_data[0]);
if (ir->dev->chip_id == CHIP_ID_EM2874) {
/* The em2874 clears the readcount field every time the
register is read. The em2860/2880 datasheet says that it
is supposed to clear the readcount, but it doesn't. So with
the em2874, we are looking for a non-zero read count as
opposed to a readcount that is incrementing */
ir->last_readcount = 0;
}
if (poll_result.read_count == 0) {
/* The button has not been pressed since the last read */
} else if (ir->last_toggle != poll_result.toggle_bit) {
/* A button has been pressed */
dprintk("button has been pressed\n");
ir->last_toggle = poll_result.toggle_bit;
ir->repeat_interval = 0;
do_sendkey = 1;
} else if (poll_result.toggle_bit == ir->last_toggle &&
poll_result.read_count > 0 &&
poll_result.read_count != ir->last_readcount) {
/* The button is still being held down */
dprintk("button being held down\n");
/* Debouncer for first keypress */
if (ir->repeat_interval++ > 9) {
/* Start repeating after 1 second */
do_sendkey = 1;
}
}
if (do_sendkey) {
dprintk("sending keypress\n");
ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
poll_result.rc_data[0]);
ir_input_nokey(ir->input, &ir->ir);
}
ir->last_readcount = poll_result.read_count;
return;
}
static void ir_timer(unsigned long data)
{
struct cx231xx_IR *ir = (struct cx231xx_IR *)data;
schedule_work(&ir->work);
}
static void cx231xx_ir_work(struct work_struct *work)
{
struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work);
cx231xx_ir_handle_key(ir);
mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
}
void cx231xx_ir_start(struct cx231xx_IR *ir)
{
setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
INIT_WORK(&ir->work, cx231xx_ir_work);
schedule_work(&ir->work);
}
static void cx231xx_ir_stop(struct cx231xx_IR *ir)
{
del_timer_sync(&ir->timer);
flush_scheduled_work();
}
int cx231xx_ir_init(struct cx231xx *dev)
{
struct cx231xx_IR *ir;
struct input_dev *input_dev;
u8 ir_config;
int err = -ENOMEM;
if (dev->board.ir_codes == NULL) {
/* No remote control support */
return 0;
}
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ir || !input_dev)
goto err_out_free;
ir->input = input_dev;
/* Setup the proper handler based on the chip */
switch (dev->chip_id) {
default:
printk("Unrecognized cx231xx chip id: IR not supported\n");
goto err_out_free;
}
/* This is how often we ask the chip for IR information */
ir->polling = 100; /* ms */
/* init input device */
snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name);
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_USB;
input_dev->id.version = 1;
input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
input_dev->dev.parent = &dev->udev->dev;
/* record handles to ourself */
ir->dev = dev;
dev->ir = ir;
cx231xx_ir_start(ir);
/* all done */
err = input_register_device(ir->input);
if (err)
goto err_out_stop;
return 0;
err_out_stop:
cx231xx_ir_stop(ir);
dev->ir = NULL;
err_out_free:
input_free_device(input_dev);
kfree(ir);
return err;
}
int cx231xx_ir_fini(struct cx231xx *dev)
{
struct cx231xx_IR *ir = dev->ir;
/* skip detach on non attached boards */
if (!ir)
return 0;
cx231xx_ir_stop(ir);
input_unregister_device(ir->input);
kfree(ir);
/* done */
dev->ir = NULL;
return 0;
}

View File

@ -0,0 +1,795 @@
/*
cx231xx-pcb-config.c - driver for Conexant
Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
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 "cx231xx.h"
#include "cx231xx-conf-reg.h"
static unsigned int pcb_debug;
module_param(pcb_debug, int, 0644);
MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
/******************************************************************************/
struct pcb_config cx231xx_Scenario[] = {
{
INDEX_SELFPOWER_DIGITAL_ONLY, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode */
SOURCE_TS_BDA, /* ts1_source, digital tv only */
NOT_SUPPORTED, /* ts2_source */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
,
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed config */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_DUAL_DIGITAL, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode */
SOURCE_TS_BDA, /* ts1_source, digital tv only */
0, /* ts2_source,need update from register */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_ANALOG_ONLY, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */
NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */
NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
0, /* analog source, need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
3, /* VANC */
4, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_DUAL, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv and digital path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
0, /* ts1_source,will update in register */
NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
0, /* analog source need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_TRIPLE, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv and digital path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
0, /* ts1_source, update in register */
0, /* ts2_source,update in register */
0, /* analog source, need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
3, /* AUDIO */
4, /* VIDEO */
5, /* VANC */
6, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
2, /* TS2 index */
3, /* AUDIO */
4, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_SELFPOWER_COMPRESSOR, /* index */
USB_SELF_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv AND DIGITAL path */
MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
NOT_SUPPORTED, /* ts1_source, disable */
SOURCE_TS_BDA, /* ts2_source */
0, /* analog source,need update */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
1, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
1, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_DIGITAL_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */
SOURCE_TS_BDA, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
NOT_SUPPORTED, /* analog source */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index = 2 */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
/* full-speed */
{
{
0, /* config index */
{
0, /* interrupt ep index = 2 */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
NOT_SUPPORTED, /* AUDIO */
NOT_SUPPORTED, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_ANALOG_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */
NOT_SUPPORTED, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
SOURCE_ANALOG, /* analog source--analog */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
3, /* VANC */
4, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
{ /* full-speed */
{
0, /* config index */
{
0, /* interrupt ep index */
NOT_SUPPORTED, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
1, /* AUDIO */
2, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
{
INDEX_BUSPOWER_DIF_ONLY, /* index */
USB_BUS_POWER, /* power_type */
0, /* speed , not decide yet */
/* mode ,analog tv AND DIGITAL path */
MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL,
SOURCE_TS_BDA, /* ts1_source, disable */
NOT_SUPPORTED, /* ts2_source */
SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */
0, /* digital_index */
0, /* analog index */
0, /* dif_index */
0, /* external_index */
1, /* only one configuration */
{
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
4, /* VANC */
5, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
,
{ /* full speed */
{
0, /* config index */
{
0, /* interrupt ep index */
1, /* ts1 index */
NOT_SUPPORTED, /* TS2 index */
2, /* AUDIO */
3, /* VIDEO */
NOT_SUPPORTED, /* VANC */
NOT_SUPPORTED, /* HANC */
NOT_SUPPORTED /* ir_index */
}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
,
{NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
NOT_SUPPORTED}
}
}
}
,
};
/*****************************************************************/
u32 initialize_cx231xx(struct cx231xx *dev)
{
u32 config_info = 0;
struct pcb_config *p_pcb_info;
u8 usb_speed = 1; /* from register,1--HS, 0--FS */
u8 data[4] = { 0, 0, 0, 0 };
u32 ts1_source = 0;
u32 ts2_source = 0;
u32 analog_source = 0;
u8 _current_scenario_idx = 0xff;
ts1_source = SOURCE_TS_BDA;
ts2_source = SOURCE_TS_BDA;
/* read board config register to find out which
pcb config it is related to */
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4);
config_info = *((u32 *) data);
usb_speed = (u8) (config_info & 0x1);
/* Verify this device belongs to Bus power or Self power device */
if (config_info & BUS_POWER) { /* bus-power */
switch (config_info & BUSPOWER_MASK) {
case TS1_PORT | BUS_POWER:
cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY;
break;
case AVDEC_ENABLE | BUS_POWER:
cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY;
break;
case AVDEC_ENABLE | BUS_POWER | TS1_PORT:
cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed =
usb_speed;
p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY];
_current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
break;
default:
cx231xx_info("bad config in buspower!!!!\n");
cx231xx_info("config_info=%x\n",
(config_info & BUSPOWER_MASK));
return 1;
}
} else { /* self-power */
switch (config_info & SELFPOWER_MASK) {
case TS1_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed =
usb_speed;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY];
_current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY;
break;
case TS1_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].
ts2_source = ts2_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL];
_current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL;
break;
case AVDEC_ENABLE | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].
analog_source = analog_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY];
_current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY;
break;
case AVDEC_ENABLE | TS1_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source =
ts1_source;
cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source =
analog_source;
p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL];
_current_scenario_idx = INDEX_SELFPOWER_DUAL;
break;
case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source =
ts1_source;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source =
ts2_source;
cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source =
analog_source;
p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE];
_current_scenario_idx = INDEX_SELFPOWER_TRIPLE;
break;
case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER:
cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed =
usb_speed;
cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].
analog_source = analog_source;
p_pcb_info =
&cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR];
_current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
break;
default:
cx231xx_info("bad senario!!!!!\n");
cx231xx_info("config_info=%x\n",
(config_info & SELFPOWER_MASK));
return 1;
}
}
dev->current_scenario_idx = _current_scenario_idx;
memcpy(&dev->current_pcb_config, p_pcb_info,
sizeof(struct pcb_config));
if (pcb_debug) {
cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
cx231xx_info("scenario %d\n",
(dev->current_pcb_config.index) + 1);
cx231xx_info("type=%x\n", dev->current_pcb_config.type);
cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
cx231xx_info("ts1_source=%x\n",
dev->current_pcb_config.ts1_source);
cx231xx_info("ts2_source=%x\n",
dev->current_pcb_config.ts2_source);
cx231xx_info("analog_source=%x\n",
dev->current_pcb_config.analog_source);
}
return 0;
}

View File

@ -0,0 +1,231 @@
/*
cx231xx-pcb-cfg.h - driver for Conexant
Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
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.
*/
#ifndef _PCB_CONFIG_H_
#define _PCB_CONFIG_H_
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************************
* Class Information *
***************************************************************************/
#define CLASS_DEFAULT 0xFF
enum VENDOR_REQUEST_TYPE {
/* Set/Get I2C */
VRT_SET_I2C0 = 0x0,
VRT_SET_I2C1 = 0x1,
VRT_SET_I2C2 = 0x2,
VRT_GET_I2C0 = 0x4,
VRT_GET_I2C1 = 0x5,
VRT_GET_I2C2 = 0x6,
/* Set/Get GPIO */
VRT_SET_GPIO = 0x8,
VRT_GET_GPIO = 0x9,
/* Set/Get GPIE */
VRT_SET_GPIE = 0xA,
VRT_GET_GPIE = 0xB,
/* Set/Get Register Control/Status */
VRT_SET_REGISTER = 0xC,
VRT_GET_REGISTER = 0xD,
/* Get Extended Compat ID Descriptor */
VRT_GET_EXTCID_DESC = 0xFF,
};
enum BYTE_ENABLE_MASK {
ENABLE_ONE_BYTE = 0x1,
ENABLE_TWE_BYTE = 0x3,
ENABLE_THREE_BYTE = 0x7,
ENABLE_FOUR_BYTE = 0xF,
};
#define SPEED_MASK 0x1
enum USB_SPEED{
FULL_SPEED = 0x0, /* 0: full speed */
HIGH_SPEED = 0x1 /* 1: high speed */
};
enum _true_false{
FALSE = 0,
TRUE = 1
};
#define TS_MASK 0x6
enum TS_PORT{
NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid,
only offers Analog TV or Video */
TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode :
Digital or External Analog/Compressed source) */
TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs
(Dual inputs from Digital and/or
External Analog/Compressed sources) */
TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector
to external clock */
TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output,
TS2 Input (from Compressor) */
};
#define EAVP_MASK 0x8
enum EAV_PRESENT{
NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
(no need for i2s blcok),
Analog Tuner must be present */
EXTERNAL_AV = 0x8 /* 1: External A/V inputs
present (requires i2s blk) */
};
#define ATM_MASK 0x30
enum AT_MODE{
DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */
BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite &
Sound-IF Signals present */
NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */
};
#define PWR_SEL_MASK 0x40
enum POWE_TYPE{
SELF_POWER = 0x0, /* 0: self power */
BUS_POWER = 0x40 /* 1: bus power */
};
enum USB_POWE_TYPE{
USB_SELF_POWER = 0,
USB_BUS_POWER
};
#define BO_0_MASK 0x80
enum AVDEC_STATUS{
AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */
AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */
};
#define BO_1_MASK 0x100
#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */
#define SELFPOWER_MASK 0x86
/***************************************************************************/
#define NOT_DECIDE_YET 0xFE
#define NOT_SUPPORTED 0xFF
/***************************************************************************
* for mod field use *
***************************************************************************/
#define MOD_DIGITAL 0x1
#define MOD_ANALOG 0x2
#define MOD_DIF 0x4
#define MOD_EXTERNAL 0x8
#define CAP_ALL_MOD 0x0f
/***************************************************************************
* source define *
***************************************************************************/
#define SOURCE_DIGITAL 0x1
#define SOURCE_ANALOG 0x2
#define SOURCE_DIF 0x4
#define SOURCE_EXTERNAL 0x8
#define SOURCE_TS_BDA 0x10
#define SOURCE_TS_ENCODE 0x20
#define SOURCE_TS_EXTERNAL 0x40
/***************************************************************************
* interface information define *
***************************************************************************/
struct INTERFACE_INFO {
u8 interrupt_index;
u8 ts1_index;
u8 ts2_index;
u8 audio_index;
u8 video_index;
u8 vanc_index; /* VBI */
u8 hanc_index; /* Sliced CC */
u8 ir_index;
};
enum INDEX_INTERFACE_INFO{
INDEX_INTERRUPT = 0x0,
INDEX_TS1,
INDEX_TS2,
INDEX_AUDIO,
INDEX_VIDEO,
INDEX_VANC,
INDEX_HANC,
INDEX_IR,
};
/***************************************************************************
* configuration information define *
***************************************************************************/
struct CONFIG_INFO {
u8 config_index;
struct INTERFACE_INFO interface_info;
};
struct pcb_config {
u8 index;
u8 type; /* bus power or self power,
self power--0, bus_power--1 */
u8 speed; /* usb speed, 2.0--1, 1.1--0 */
u8 mode; /* digital , anlog, dif or external A/V */
u32 ts1_source; /* three source -- BDA,External,encode */
u32 ts2_source;
u32 analog_source;
u8 digital_index; /* bus-power used */
u8 analog_index; /* bus-power used */
u8 dif_index; /* bus-power used */
u8 external_index; /* bus-power used */
u8 config_num; /* current config num, 0,1,2,
for self-power, always 0 */
struct CONFIG_INFO hs_config_info[3];
struct CONFIG_INFO fs_config_info[3];
};
enum INDEX_PCB_CONFIG{
INDEX_SELFPOWER_DIGITAL_ONLY = 0x0,
INDEX_SELFPOWER_DUAL_DIGITAL,
INDEX_SELFPOWER_ANALOG_ONLY,
INDEX_SELFPOWER_DUAL,
INDEX_SELFPOWER_TRIPLE,
INDEX_SELFPOWER_COMPRESSOR,
INDEX_BUSPOWER_DIGITAL_ONLY,
INDEX_BUSPOWER_ANALOG_ONLY,
INDEX_BUSPOWER_DIF_ONLY,
INDEX_BUSPOWER_EXTERNAL_ONLY,
INDEX_BUSPOWER_EXTERNAL_ANALOG,
INDEX_BUSPOWER_EXTERNAL_DIF,
INDEX_BUSPOWER_EXTERNAL_DIGITAL,
INDEX_BUSPOWER_DIGITAL_ANALOG,
INDEX_BUSPOWER_DIGITAL_DIF,
INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL,
INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL,
};
/***************************************************************************/
struct cx231xx;
u32 initialize_cx231xx(struct cx231xx *p_dev);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,701 @@
/*
cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on cx88 driver
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bitmap.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
#include "cx231xx.h"
#include "cx231xx-vbi.h"
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
{
char *errmsg = "Unknown";
switch (status) {
case -ENOENT:
errmsg = "unlinked synchronuously";
break;
case -ECONNRESET:
errmsg = "unlinked asynchronuously";
break;
case -ENOSR:
errmsg = "Buffer error (overrun)";
break;
case -EPIPE:
errmsg = "Stalled (device not responding)";
break;
case -EOVERFLOW:
errmsg = "Babble (bad cable?)";
break;
case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)";
break;
case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)";
break;
case -ETIME:
errmsg = "Device does not respond";
break;
}
if (packet < 0) {
cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status,
errmsg);
} else {
cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
/*
* Controls the isoc copy of each urb packet
*/
static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
{
struct cx231xx_buffer *buf;
struct cx231xx_dmaqueue *dma_q = urb->context;
int rc = 1;
unsigned char *p_buffer;
u32 bytes_parsed = 0, buffer_size = 0;
u8 sav_eav = 0;
if (!dev)
return 0;
if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
buf = dev->vbi_mode.isoc_ctl.buf;
/* get buffer pointer and length */
p_buffer = urb->transfer_buffer;
buffer_size = urb->actual_length;
if (buffer_size > 0) {
bytes_parsed = 0;
if (dma_q->is_partial_line) {
/* Handle the case where we were working on a partial
line */
sav_eav = dma_q->last_sav;
} else {
/* Check for a SAV/EAV overlapping the
buffer boundary */
sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
dma_q->partial_buf,
&bytes_parsed);
}
sav_eav &= 0xF0;
/* Get the first line if we have some portion of an SAV/EAV from
the last buffer or a partial line */
if (sav_eav) {
bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
sav_eav, /* SAV/EAV */
p_buffer + bytes_parsed, /* p_buffer */
buffer_size - bytes_parsed); /* buffer size */
}
/* Now parse data that is completely in this buffer */
dma_q->is_partial_line = 0;
while (bytes_parsed < buffer_size) {
u32 bytes_used = 0;
sav_eav = cx231xx_find_next_SAV_EAV(
p_buffer + bytes_parsed, /* p_buffer */
buffer_size - bytes_parsed, /* buffer size */
&bytes_used); /* bytes used to get SAV/EAV */
bytes_parsed += bytes_used;
sav_eav &= 0xF0;
if (sav_eav && (bytes_parsed < buffer_size)) {
bytes_parsed += cx231xx_get_vbi_line(dev,
dma_q, sav_eav, /* SAV/EAV */
p_buffer+bytes_parsed, /* p_buffer */
buffer_size-bytes_parsed);/*buf size*/
}
}
/* Save the last four bytes of the buffer so we can
check the buffer boundary condition next time */
memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
bytes_parsed = 0;
}
return rc;
}
/* ------------------------------------------------------------------
Vbi buf operations
------------------------------------------------------------------*/
static int
vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
*size = (dev->width * height * 2);
if (0 == *count)
*count = CX231XX_DEF_VBI_BUF;
if (*count < CX231XX_MIN_BUF)
*count = CX231XX_MIN_BUF;
return 0;
}
/* This is called *without* dev->slock held; please keep it that way */
static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt())
BUG();
/* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->vbi_mode.slock, flags);
if (dev->vbi_mode.isoc_ctl.buf == buf)
dev->vbi_mode.isoc_ctl.buf = NULL;
spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
static int
vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
struct cx231xx *dev = fh->dev;
int rc = 0, urb_init = 0;
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
buf->vb.size = ((dev->width << 1) * height);
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
buf->vb.width = dev->width;
buf->vb.height = height;
buf->vb.field = field;
buf->vb.field = V4L2_FIELD_SEQ_TB;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(vq, &buf->vb, NULL);
if (rc < 0)
goto fail;
}
if (!dev->vbi_mode.isoc_ctl.num_bufs)
urb_init = 1;
if (urb_init) {
rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
CX231XX_NUM_VBI_BUFS,
dev->vbi_mode.alt_max_pkt_size[0],
cx231xx_isoc_vbi_copy);
if (rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(vq, buf);
return rc;
}
static void
vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
struct cx231xx_fh *fh = vq->priv_data;
struct cx231xx *dev = fh->dev;
struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vidq->active);
}
static void vbi_buffer_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct cx231xx_buffer *buf =
container_of(vb, struct cx231xx_buffer, vb);
free_buffer(vq, buf);
}
struct videobuf_queue_ops cx231xx_vbi_qops = {
.buf_setup = vbi_buffer_setup,
.buf_prepare = vbi_buffer_prepare,
.buf_queue = vbi_buffer_queue,
.buf_release = vbi_buffer_release,
};
/* ------------------------------------------------------------------
URB control
------------------------------------------------------------------*/
/*
* IRQ callback, called by URB callback
*/
static void cx231xx_irq_vbi_callback(struct urb *urb)
{
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, vbi_mode);
int rc;
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
cx231xx_err(DRIVER_NAME "urb completition error %d.\n",
urb->status);
break;
}
/* Copy data from URB */
spin_lock(&dev->vbi_mode.slock);
rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb);
spin_unlock(&dev->vbi_mode.slock);
/* Reset status */
urb->status = 0;
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n",
urb->status);
}
}
/*
* Stop and Deallocate URBs
*/
void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
{
struct urb *urb;
int i;
cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n");
dev->vbi_mode.isoc_ctl.nfields = -1;
for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
urb = dev->vbi_mode.isoc_ctl.urb[i];
if (urb) {
if (!irqs_disabled())
usb_kill_urb(urb);
else
usb_unlink_urb(urb);
if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
kfree(dev->vbi_mode.isoc_ctl.
transfer_buffer[i]);
dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
NULL;
}
usb_free_urb(urb);
dev->vbi_mode.isoc_ctl.urb[i] = NULL;
}
dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL;
}
kfree(dev->vbi_mode.isoc_ctl.urb);
kfree(dev->vbi_mode.isoc_ctl.transfer_buffer);
dev->vbi_mode.isoc_ctl.urb = NULL;
dev->vbi_mode.isoc_ctl.transfer_buffer = NULL;
dev->vbi_mode.isoc_ctl.num_bufs = 0;
cx231xx_capture_start(dev, 0, Vbi);
}
EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
/*
* Allocate URBs and start IRQ
*/
int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct cx231xx *dev,
struct urb *urb))
{
struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
int i;
int sb_size, pipe;
struct urb *urb;
int rc;
cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n");
/* De-allocates all pending stuff */
cx231xx_uninit_vbi_isoc(dev);
/* clear if any halt */
usb_clear_halt(dev->udev,
usb_rcvbulkpipe(dev->udev,
dev->vbi_mode.end_point_addr));
dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy;
dev->vbi_mode.isoc_ctl.num_bufs = num_bufs;
dma_q->pos = 0;
dma_q->is_partial_line = 0;
dma_q->last_sav = 0;
dma_q->current_field = -1;
dma_q->bytes_left_in_line = dev->width << 1;
dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
dma_q->lines_completed = 0;
for (i = 0; i < 8; i++)
dma_q->partial_buf[i] = 0;
dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
GFP_KERNEL);
if (!dev->vbi_mode.isoc_ctl.urb) {
cx231xx_errdev("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->vbi_mode.isoc_ctl.transfer_buffer =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->vbi_mode.isoc_ctl.transfer_buffer) {
cx231xx_errdev("cannot allocate memory for usbtransfer\n");
kfree(dev->vbi_mode.isoc_ctl.urb);
return -ENOMEM;
}
dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size;
dev->vbi_mode.isoc_ctl.buf = NULL;
sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size;
/* allocate urbs and transfer buffers */
for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
cx231xx_err(DRIVER_NAME
": cannot alloc isoc_ctl.urb %i\n", i);
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
dev->vbi_mode.isoc_ctl.urb[i] = urb;
urb->transfer_flags = 0;
dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
kzalloc(sb_size, GFP_KERNEL);
if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
cx231xx_err(DRIVER_NAME
": unable to allocate %i bytes for transfer"
" buffer %i%s\n", sb_size, i,
in_interrupt() ? " while in int" : "");
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
usb_fill_bulk_urb(urb, dev->udev, pipe,
dev->vbi_mode.isoc_ctl.transfer_buffer[i],
sb_size, cx231xx_irq_vbi_callback, dma_q);
}
init_waitqueue_head(&dma_q->wq);
/* submit urbs and enables IRQ */
for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC);
if (rc) {
cx231xx_err(DRIVER_NAME
": submit of urb %i failed (error=%i)\n", i,
rc);
cx231xx_uninit_vbi_isoc(dev);
return rc;
}
}
cx231xx_capture_start(dev, 1, Vbi);
return 0;
}
EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size)
{
u32 bytes_copied = 0;
int current_field = -1;
switch (sav_eav) {
case SAV_VBI_FIELD1:
current_field = 1;
break;
case SAV_VBI_FIELD2:
current_field = 2;
break;
default:
break;
}
if (current_field < 0)
return bytes_copied;
dma_q->last_sav = sav_eav;
bytes_copied =
cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
current_field);
return bytes_copied;
}
/*
* Announces that a buffer were filled and request the next
*/
static inline void vbi_buffer_filled(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q,
struct cx231xx_buffer *buf)
{
/* Advice that buffer was filled */
/* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
do_gettimeofday(&buf->vb.ts);
dev->vbi_mode.isoc_ctl.buf = NULL;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number)
{
u32 bytes_to_copy;
struct cx231xx_buffer *buf;
u32 _line_size = dev->width * 2;
if (dma_q->current_field != field_number)
cx231xx_reset_vbi_buffer(dev, dma_q);
/* get the buffer pointer */
buf = dev->vbi_mode.isoc_ctl.buf;
/* Remember the field number for next time */
dma_q->current_field = field_number;
bytes_to_copy = dma_q->bytes_left_in_line;
if (bytes_to_copy > length)
bytes_to_copy = length;
if (dma_q->lines_completed >= dma_q->lines_per_field) {
dma_q->bytes_left_in_line -= bytes_to_copy;
dma_q->is_partial_line =
(dma_q->bytes_left_in_line == 0) ? 0 : 1;
return 0;
}
dma_q->is_partial_line = 1;
/* If we don't have a buffer, just return the number of bytes we would
have copied if we had a buffer. */
if (!buf) {
dma_q->bytes_left_in_line -= bytes_to_copy;
dma_q->is_partial_line =
(dma_q->bytes_left_in_line == 0) ? 0 : 1;
return bytes_to_copy;
}
/* copy the data to video buffer */
cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
dma_q->pos += bytes_to_copy;
dma_q->bytes_left_in_line -= bytes_to_copy;
if (dma_q->bytes_left_in_line == 0) {
dma_q->bytes_left_in_line = _line_size;
dma_q->lines_completed++;
dma_q->is_partial_line = 0;
if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
vbi_buffer_filled(dev, dma_q, buf);
dma_q->pos = 0;
buf = NULL;
dma_q->lines_completed = 0;
}
}
return bytes_to_copy;
}
/*
* video-buf generic routine to get the next available buffer
*/
static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
struct cx231xx_buffer **buf)
{
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
char *outp;
if (list_empty(&dma_q->active)) {
cx231xx_err(DRIVER_NAME ": No active queue to serve\n");
dev->vbi_mode.isoc_ctl.buf = NULL;
*buf = NULL;
return;
}
/* Get the next buffer */
*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
/* Cleans up buffer - Usefull for testing for frame/URB loss */
outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0, (*buf)->vb.size);
dev->vbi_mode.isoc_ctl.buf = *buf;
return;
}
void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q)
{
struct cx231xx_buffer *buf;
buf = dev->vbi_mode.isoc_ctl.buf;
if (buf == NULL) {
/* first try to get the buffer */
get_next_vbi_buf(dma_q, &buf);
dma_q->pos = 0;
dma_q->current_field = -1;
}
dma_q->bytes_left_in_line = dev->width << 1;
dma_q->lines_completed = 0;
}
int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy)
{
u8 *p_out_buffer = NULL;
u32 current_line_bytes_copied = 0;
struct cx231xx_buffer *buf;
u32 _line_size = dev->width << 1;
void *startwrite;
int offset, lencopy;
buf = dev->vbi_mode.isoc_ctl.buf;
if (buf == NULL)
return -EINVAL;
p_out_buffer = videobuf_to_vmalloc(&buf->vb);
if (dma_q->bytes_left_in_line != _line_size) {
current_line_bytes_copied =
_line_size - dma_q->bytes_left_in_line;
}
offset = (dma_q->lines_completed * _line_size) +
current_line_bytes_copied;
/* prepare destination address */
startwrite = p_out_buffer + offset;
lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
bytes_to_copy : dma_q->bytes_left_in_line;
memcpy(startwrite, p_buffer, lencopy);
return 0;
}
u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q)
{
u32 height = 0;
height = ((dev->norm & V4L2_STD_625_50) ?
PAL_VBI_LINES : NTSC_VBI_LINES);
return (dma_q->lines_completed == height) ? 1 : 0;
}

View File

@ -0,0 +1,65 @@
/*
cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on cx88 driver
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.
*/
#ifndef _CX231XX_VBI_H
#define _CX231XX_VBI_H
extern struct videobuf_queue_ops cx231xx_vbi_qops;
#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
#define NTSC_VBI_END_LINE 21
#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1)
#define PAL_VBI_START_LINE 6
#define PAL_VBI_END_LINE 23
#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1)
#define VBI_STRIDE 1440
#define VBI_SAMPLES_PER_LINE 1440
#define CX231XX_NUM_VBI_PACKETS 4
#define CX231XX_NUM_VBI_BUFS 5
/* stream functions */
int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct cx231xx *dev,
struct urb *urb));
void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
/* vbi data copy functions */
u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size);
u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number);
void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy);
u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,779 @@
/*
cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices
Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
Based on em28xx driver
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.
*/
#ifndef _CX231XX_H
#define _CX231XX_H
#include <linux/videodev2.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/mutex.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/ir-kbd-i2c.h>
#if defined(CONFIG_VIDEO_CX231XX_DVB) || \
defined(CONFIG_VIDEO_CX231XX_DVB_MODULE)
#include <media/videobuf-dvb.h>
#endif
#include "cx231xx-reg.h"
#include "cx231xx-pcb-cfg.h"
#include "cx231xx-conf-reg.h"
#define DRIVER_NAME "cx231xx"
#define PWR_SLEEP_INTERVAL 5
/* I2C addresses for control block in Cx231xx */
#define AFE_DEVICE_ADDRESS 0x60
#define I2S_BLK_DEVICE_ADDRESS 0x98
#define VID_BLK_I2C_ADDRESS 0x88
#define DIF_USE_BASEBAND 0xFFFFFFFF
/* Boards supported by driver */
#define CX231XX_BOARD_UNKNOWN 0
#define CX231XX_BOARD_CNXT_RDE_250 1
#define CX231XX_BOARD_CNXT_RDU_250 2
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
#define CX231XX_DEF_BUF 12
#define CX231XX_DEF_VBI_BUF 6
#define VBI_LINE_COUNT 17
#define VBI_LINE_LENGTH 1440
/*Limits the max URB message size */
#define URB_MAX_CTRL_SIZE 80
/* Params for validated field */
#define CX231XX_BOARD_NOT_VALIDATED 1
#define CX231XX_BOARD_VALIDATED 0
/* maximum number of cx231xx boards */
#define CX231XX_MAXBOARDS 8
/* maximum number of frames that can be queued */
#define CX231XX_NUM_FRAMES 5
/* number of buffers for isoc transfers */
#define CX231XX_NUM_BUFS 8
/* number of packets for each buffer
windows requests only 40 packets .. so we better do the same
this is what I found out for all alternate numbers there!
*/
#define CX231XX_NUM_PACKETS 40
/* default alternate; 0 means choose the best */
#define CX231XX_PINOUT 0
#define CX231XX_INTERLACED_DEFAULT 1
/* time to wait when stopping the isoc transfer */
#define CX231XX_URB_TIMEOUT \
msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS)
enum cx231xx_mode {
CX231XX_SUSPEND,
CX231XX_ANALOG_MODE,
CX231XX_DIGITAL_MODE,
};
enum cx231xx_std_mode {
CX231XX_TV_AIR = 0,
CX231XX_TV_CABLE
};
enum cx231xx_stream_state {
STREAM_OFF,
STREAM_INTERRUPT,
STREAM_ON,
};
struct cx231xx;
struct cx231xx_usb_isoc_ctl {
/* max packet size of isoc transaction */
int max_pkt_size;
/* number of allocated urbs */
int num_bufs;
/* urb for isoc transfers */
struct urb **urb;
/* transfer buffers for isoc transfer */
char **transfer_buffer;
/* Last buffer command and region */
u8 cmd;
int pos, size, pktsize;
/* Last field: ODD or EVEN? */
int field;
/* Stores incomplete commands */
u32 tmp_buf;
int tmp_buf_len;
/* Stores already requested buffers */
struct cx231xx_buffer *buf;
/* Stores the number of received fields */
int nfields;
/* isoc urb callback */
int (*isoc_copy) (struct cx231xx *dev, struct urb *urb);
};
struct cx231xx_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
int depth;
int reg;
};
/* buffer for one video frame */
struct cx231xx_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
struct list_head frame;
int top_field;
int receiving;
};
struct cx231xx_dmaqueue {
struct list_head active;
struct list_head queued;
wait_queue_head_t wq;
/* Counters to control buffer fill */
int pos;
u8 is_partial_line;
u8 partial_buf[8];
u8 last_sav;
int current_field;
u32 bytes_left_in_line;
u32 lines_completed;
u8 field1_done;
u32 lines_per_field;
};
/* inputs */
#define MAX_CX231XX_INPUT 4
enum cx231xx_itype {
CX231XX_VMUX_COMPOSITE1 = 1,
CX231XX_VMUX_SVIDEO,
CX231XX_VMUX_TELEVISION,
CX231XX_VMUX_CABLE,
CX231XX_RADIO,
CX231XX_VMUX_DVB,
CX231XX_VMUX_DEBUG
};
enum cx231xx_v_input {
CX231XX_VIN_1_1 = 0x1,
CX231XX_VIN_2_1,
CX231XX_VIN_3_1,
CX231XX_VIN_4_1,
CX231XX_VIN_1_2 = 0x01,
CX231XX_VIN_2_2,
CX231XX_VIN_3_2,
CX231XX_VIN_1_3 = 0x1,
CX231XX_VIN_2_3,
CX231XX_VIN_3_3,
};
/* cx231xx has two audio inputs: tuner and line in */
enum cx231xx_amux {
/* This is the only entry for cx231xx tuner input */
CX231XX_AMUX_VIDEO, /* cx231xx tuner */
CX231XX_AMUX_LINE_IN, /* Line In */
};
struct cx231xx_reg_seq {
unsigned char bit;
unsigned char val;
int sleep;
};
struct cx231xx_input {
enum cx231xx_itype type;
unsigned int vmux;
enum cx231xx_amux amux;
struct cx231xx_reg_seq *gpio;
};
#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr])
enum cx231xx_decoder {
CX231XX_NODECODER,
CX231XX_AVDECODER
};
enum CX231XX_I2C_MASTER_PORT {
I2C_0 = 0,
I2C_1 = 1,
I2C_2 = 2,
I2C_3 = 3
};
struct cx231xx_board {
char *name;
int vchannels;
int tuner_type;
int tuner_addr;
v4l2_std_id norm; /* tv norm */
/* demod related */
int demod_addr;
u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */
/* GPIO Pins */
struct cx231xx_reg_seq *dvb_gpio;
struct cx231xx_reg_seq *suspend_gpio;
struct cx231xx_reg_seq *tuner_gpio;
u8 tuner_sif_gpio;
u8 tuner_scl_gpio;
u8 tuner_sda_gpio;
/* PIN ctrl */
u32 ctl_pin_status_mask;
u8 agc_analog_digital_select_gpio;
u32 gpio_pin_status_mask;
/* i2c masters */
u8 tuner_i2c_master;
u8 demod_i2c_master;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
unsigned int valid:1;
unsigned char xclk, i2c_speed;
enum cx231xx_decoder decoder;
struct cx231xx_input input[MAX_CX231XX_INPUT];
struct cx231xx_input radio;
IR_KEYTAB_TYPE *ir_codes;
};
/* device states */
enum cx231xx_dev_state {
DEV_INITIALIZED = 0x01,
DEV_DISCONNECTED = 0x02,
DEV_MISCONFIGURED = 0x04,
};
enum AFE_MODE {
AFE_MODE_LOW_IF,
AFE_MODE_BASEBAND,
AFE_MODE_EU_HI_IF,
AFE_MODE_US_HI_IF,
AFE_MODE_JAPAN_HI_IF
};
enum AUDIO_INPUT {
AUDIO_INPUT_MUTE,
AUDIO_INPUT_LINE,
AUDIO_INPUT_TUNER_TV,
AUDIO_INPUT_SPDIF,
AUDIO_INPUT_TUNER_FM
};
#define CX231XX_AUDIO_BUFS 5
#define CX231XX_NUM_AUDIO_PACKETS 64
#define CX231XX_CAPTURE_STREAM_EN 1
#define CX231XX_STOP_AUDIO 0
#define CX231XX_START_AUDIO 1
/* cx231xx extensions */
#define CX231XX_AUDIO 0x10
#define CX231XX_DVB 0x20
struct cx231xx_audio {
char name[50];
char *transfer_buffer[CX231XX_AUDIO_BUFS];
struct urb *urb[CX231XX_AUDIO_BUFS];
struct usb_device *udev;
unsigned int capture_transfer_done;
struct snd_pcm_substream *capture_pcm_substream;
unsigned int hwptr_done_capture;
struct snd_card *sndcard;
int users, shutdown;
enum cx231xx_stream_state capture_stream;
spinlock_t slock;
int alt; /* alternate */
int max_pkt_size; /* max packet size of isoc transaction */
int num_alt; /* Number of alternative settings */
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
u16 end_point_addr;
};
struct cx231xx;
struct cx231xx_fh {
struct cx231xx *dev;
unsigned int stream_on:1; /* Locks streams */
int radio;
struct videobuf_queue vb_vidq;
enum v4l2_buf_type type;
};
/*****************************************************************/
/* set/get i2c */
/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */
#define I2C_SPEED_1M 0x0
#define I2C_SPEED_400K 0x1
#define I2C_SPEED_100K 0x2
#define I2C_SPEED_5M 0x3
/* 0-- STOP transaction */
#define I2C_STOP 0x0
/* 1-- do not transmit STOP at end of transaction */
#define I2C_NOSTOP 0x1
/* 1--alllow slave to insert clock wait states */
#define I2C_SYNC 0x1
struct cx231xx_i2c {
struct cx231xx *dev;
int nr;
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
/* different settings for each bus */
u8 i2c_period;
u8 i2c_nostop;
u8 i2c_reserve;
};
struct cx231xx_i2c_xfer_data {
u8 dev_addr;
u8 direction; /* 1 - IN, 0 - OUT */
u8 saddr_len; /* sub address len */
u16 saddr_dat; /* sub addr data */
u8 buf_size; /* buffer size */
u8 *p_buffer; /* pointer to the buffer */
};
struct VENDOR_REQUEST_IN {
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
u8 direction;
u8 bData;
u8 *pBuff;
};
struct cx231xx_ctrl {
struct v4l2_queryctrl v;
u32 off;
u32 reg;
u32 mask;
u32 shift;
};
enum TRANSFER_TYPE {
Raw_Video = 0,
Audio,
Vbi, /* VANC */
Sliced_cc, /* HANC */
TS1_serial_mode,
TS2,
TS1_parallel_mode
} ;
struct cx231xx_video_mode {
/* Isoc control struct */
struct cx231xx_dmaqueue vidq;
struct cx231xx_usb_isoc_ctl isoc_ctl;
spinlock_t slock;
/* usb transfer */
int alt; /* alternate */
int max_pkt_size; /* max packet size of isoc transaction */
int num_alt; /* Number of alternative settings */
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
u16 end_point_addr;
};
/* main device struct */
struct cx231xx {
/* generic device properties */
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
struct cx231xx_board board;
unsigned int stream_on:1; /* Locks streams */
unsigned int vbi_stream_on:1; /* Locks streams for VBI */
unsigned int has_audio_class:1;
unsigned int has_alsa_audio:1;
struct cx231xx_fmt *format;
struct v4l2_device v4l2_dev;
struct v4l2_subdev *sd_cx25840;
struct v4l2_subdev *sd_tuner;
struct cx231xx_IR *ir;
struct list_head devlist;
int tuner_type; /* type of the tuner */
int tuner_addr; /* tuner address */
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx231xx_i2c i2c_bus[3];
unsigned int xc_fw_load_done:1;
struct mutex gpio_i2c_lock;
/* video for linux */
int users; /* user count for exclusive use */
struct video_device *vdev; /* video for linux device struct */
v4l2_std_id norm; /* selected tv norm */
int ctl_freq; /* selected frequency */
unsigned int ctl_ainput; /* selected audio input */
int mute;
int volume;
/* frame properties */
int width; /* current frame width */
int height; /* current frame height */
unsigned hscale; /* horizontal scale factor (see datasheet) */
unsigned vscale; /* vertical scale factor (see datasheet) */
int interlaced; /* 1=interlace fileds, 0=just top fileds */
struct cx231xx_audio adev;
/* states */
enum cx231xx_dev_state state;
struct work_struct request_module_wk;
/* locks */
struct mutex lock;
struct mutex ctrl_urb_lock; /* protects urb_buf */
struct list_head inqueue, outqueue;
wait_queue_head_t open, wait_frame, wait_stream;
struct video_device *vbi_dev;
struct video_device *radio_dev;
unsigned char eedata[256];
struct cx231xx_video_mode video_mode;
struct cx231xx_video_mode vbi_mode;
struct cx231xx_video_mode sliced_cc_mode;
struct cx231xx_video_mode ts1_mode;
struct usb_device *udev; /* the usb device */
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
/* helper funcs that call usb_control_msg */
int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus,
struct cx231xx_i2c_xfer_data *req_data);
int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr,
u8 *buf, u8 len);
int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr,
u8 *buf, u8 len);
int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq);
int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev);
enum cx231xx_mode mode;
struct cx231xx_dvb *dvb;
/* Cx231xx supported PCB config's */
struct pcb_config current_pcb_config;
u8 current_scenario_idx;
u8 interface_count;
u8 max_iad_interface_count;
/* GPIO related register direction and values */
u32 gpio_dir;
u32 gpio_val;
/* Power Modes */
int power_mode;
/* afe parameters */
enum AFE_MODE afe_mode;
u32 afe_ref_count;
/* video related parameters */
u32 video_input;
u32 active_mode;
u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */
enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */
};
#define cx25840_call(cx231xx, o, f, args...) \
v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args)
#define tuner_call(cx231xx, o, f, args...) \
v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args)
#define call_all(dev, o, f, args...) \
v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
struct cx231xx_ops {
struct list_head next;
char *name;
int id;
int (*init) (struct cx231xx *);
int (*fini) (struct cx231xx *);
};
/* call back functions in dvb module */
int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq);
int cx231xx_reset_analog_tuner(struct cx231xx *dev);
/* Provided by cx231xx-i2c.c */
void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c);
int cx231xx_i2c_register(struct cx231xx_i2c *bus);
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
/* Internal block control functions */
int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr,
u16 saddr, u8 saddr_len, u32 *data, u8 data_len);
int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr,
u16 saddr, u8 saddr_len, u32 data, u8 data_len);
int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
u16 register_address, u8 bit_start, u8 bit_end,
u32 value);
int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
u16 saddr, u32 mask, u32 value);
u32 cx231xx_set_field(u32 field_mask, u32 data);
/* afe related functions */
int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count);
int cx231xx_afe_init_channels(struct cx231xx *dev);
int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev);
int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux);
int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode);
int cx231xx_afe_update_power_control(struct cx231xx *dev,
enum AV_MODE avmode);
int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input);
/* i2s block related functions */
int cx231xx_i2s_blk_initialize(struct cx231xx *dev);
int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
enum AV_MODE avmode);
int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input);
/* DIF related functions */
int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
u32 function_mode, u32 standard);
int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard);
int cx231xx_tuner_pre_channel_change(struct cx231xx *dev);
int cx231xx_tuner_post_channel_change(struct cx231xx *dev);
/* video parser functions */
u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size,
u32 *p_bytes_used);
u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
u32 *p_bytes_used);
int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_buffer, u32 bytes_to_copy);
void cx231xx_reset_video_buffer(struct cx231xx *dev,
struct cx231xx_dmaqueue *dma_q);
u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q);
u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 *p_line, u32 length, int field_number);
u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
u8 sav_eav, u8 *p_buffer, u32 buffer_size);
void cx231xx_swab(u16 *from, u16 *to, u16 len);
/* Provided by cx231xx-core.c */
u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count);
void cx231xx_queue_unusedframes(struct cx231xx *dev);
void cx231xx_release_buffers(struct cx231xx *dev);
/* read from control pipe */
int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
/* write to control pipe */
int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
char *buf, int len);
int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode);
int cx231xx_send_vendor_cmd(struct cx231xx *dev,
struct VENDOR_REQUEST_IN *ven_req);
int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
struct cx231xx_i2c_xfer_data *req_data);
/* Gpio related functions */
int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
u8 len, u8 request, u8 direction);
int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value);
int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number,
int pin_value);
int cx231xx_gpio_i2c_start(struct cx231xx *dev);
int cx231xx_gpio_i2c_end(struct cx231xx *dev);
int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data);
int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf);
int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev);
int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev);
int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev);
int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
/* audio related functions */
int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
enum AUDIO_INPUT audio_input);
int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type);
int cx231xx_resolution_set(struct cx231xx *dev);
int cx231xx_set_video_alternate(struct cx231xx *dev);
int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt);
int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct cx231xx *dev,
struct urb *urb));
void cx231xx_uninit_isoc(struct cx231xx *dev);
int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode);
int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio);
/* Device list functions */
void cx231xx_release_resources(struct cx231xx *dev);
void cx231xx_release_analog_resources(struct cx231xx *dev);
int cx231xx_register_analog_devices(struct cx231xx *dev);
void cx231xx_remove_from_devlist(struct cx231xx *dev);
void cx231xx_add_into_devlist(struct cx231xx *dev);
struct cx231xx *cx231xx_get_device(int minor,
enum v4l2_buf_type *fh_type, int *has_radio);
void cx231xx_init_extension(struct cx231xx *dev);
void cx231xx_close_extension(struct cx231xx *dev);
/* hardware init functions */
int cx231xx_dev_init(struct cx231xx *dev);
void cx231xx_dev_uninit(struct cx231xx *dev);
void cx231xx_config_i2c(struct cx231xx *dev);
int cx231xx_config(struct cx231xx *dev);
/* Stream control functions */
int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask);
int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask);
int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type);
/* Power control functions */
int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode);
int cx231xx_power_suspend(struct cx231xx *dev);
/* chip specific control functions */
int cx231xx_init_ctrl_pin_status(struct cx231xx *dev);
int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
u8 analog_or_digital);
int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex);
/* video audio decoder related functions */
void video_mux(struct cx231xx *dev, int index);
int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input);
int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input);
int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev);
int cx231xx_set_audio_input(struct cx231xx *dev, u8 input);
void get_scale(struct cx231xx *dev,
unsigned int width, unsigned int height,
unsigned int *hscale, unsigned int *vscale);
/* Provided by cx231xx-video.c */
int cx231xx_register_extension(struct cx231xx_ops *dev);
void cx231xx_unregister_extension(struct cx231xx_ops *dev);
void cx231xx_init_extension(struct cx231xx *dev);
void cx231xx_close_extension(struct cx231xx *dev);
/* Provided by cx231xx-cards.c */
extern void cx231xx_pre_card_setup(struct cx231xx *dev);
extern void cx231xx_card_setup(struct cx231xx *dev);
extern struct cx231xx_board cx231xx_boards[];
extern struct usb_device_id cx231xx_id_table[];
extern const unsigned int cx231xx_bcount;
void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir);
int cx231xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by cx231xx-input.c */
int cx231xx_ir_init(struct cx231xx *dev);
int cx231xx_ir_fini(struct cx231xx *dev);
/* printk macros */
#define cx231xx_err(fmt, arg...) do {\
printk(KERN_ERR fmt , ##arg); } while (0)
#define cx231xx_errdev(fmt, arg...) do {\
printk(KERN_ERR "%s: "fmt,\
dev->name , ##arg); } while (0)
#define cx231xx_info(fmt, arg...) do {\
printk(KERN_INFO "%s: "fmt,\
dev->name , ##arg); } while (0)
#define cx231xx_warn(fmt, arg...) do {\
printk(KERN_WARNING "%s: "fmt,\
dev->name , ##arg); } while (0)
static inline unsigned int norm_maxw(struct cx231xx *dev)
{
if (dev->board.max_range_640_480)
return 640;
else
return 720;
}
static inline unsigned int norm_maxh(struct cx231xx *dev)
{
if (dev->board.max_range_640_480)
return 480;
else
return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
}
#endif

View File

@ -739,9 +739,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->i2c_bus[2].i2c_adap,
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1);
v4l2_subdev_call(dev->sd_cx25840, core, init, 0);
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
break;
}

View File

@ -875,7 +875,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_i2c_register(&dev->i2c_bus[1]);
cx23885_i2c_register(&dev->i2c_bus[2]);
cx23885_card_setup(dev);
call_all(dev, core, s_standby, 0);
call_all(dev, tuner, s_standby);
cx23885_ir_init(dev);
if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {

View File

@ -673,7 +673,7 @@ static int dvb_register(struct cx23885_tsport *port)
fe0->dvb.frontend->callback = cx23885_tuner_callback;
/* Put the analog decoder in standby to keep it quiet */
call_all(dev, core, s_standby, 0);
call_all(dev, tuner, s_standby);
if (fe0->dvb.frontend->ops.analog_ops.standby)
fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);

View File

@ -299,7 +299,7 @@ static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
dev->tvnorm = norm;
call_all(dev, tuner, s_std, norm);
call_all(dev, core, s_std, norm);
return 0;
}
@ -393,9 +393,6 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
{
struct v4l2_routing route;
memset(&route, 0, sizeof(route));
dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
__func__,
input, INPUT(input)->vmux,
@ -403,10 +400,9 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
INPUT(input)->gpio2, INPUT(input)->gpio3);
dev->input = input;
route.input = INPUT(input)->vmux;
/* Tell the internal A/V decoder */
v4l2_subdev_call(dev->sd_cx25840, video, s_routing, &route);
v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
INPUT(input)->vmux, 0, 0);
return 0;
}
@ -1523,10 +1519,12 @@ int cx23885_video_register(struct cx23885_dev *dev)
struct v4l2_subdev *sd = NULL;
if (dev->tuner_addr)
sd = v4l2_i2c_new_subdev(&dev->i2c_bus[1].i2c_adap,
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", dev->tuner_addr);
else
sd = v4l2_i2c_new_probed_subdev(&dev->i2c_bus[1].i2c_adap,
sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV));
if (sd) {
struct tuner_setup tun_setup;

View File

@ -37,7 +37,7 @@
#include <linux/version.h>
#include <linux/mutex.h>
#define CX23885_VERSION_CODE KERNEL_VERSION(0, 0, 1)
#define CX23885_VERSION_CODE KERNEL_VERSION(0, 0, 2)
#define UNSET (-1U)

View File

@ -32,7 +32,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
/* common for all inputs and rates */
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
if (!state->is_cx23885)
if (!state->is_cx23885 && !state->is_cx231xx)
cx25840_write(client, 0x127, 0x50);
if (state->aud_input != CX25840_AUDIO_SERIAL) {
@ -43,11 +43,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
* so avoid destroying registers. */
break;
}
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1006040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x01bb39ee);
if (!state->is_cx231xx) {
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1006040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x01bb39ee);
}
if (state->is_cx25836)
break;
@ -64,11 +67,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
* so avoid destroying registers. */
break;
}
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1009040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x00ec6bd6);
if (!state->is_cx231xx) {
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1009040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x00ec6bd6);
}
if (state->is_cx25836)
break;
@ -85,11 +91,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
* so avoid destroying registers. */
break;
}
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x100a040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x0098d6e5);
if (!state->is_cx231xx) {
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x100a040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x0098d6e5);
}
if (state->is_cx25836)
break;
@ -108,11 +117,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
* so avoid destroying registers. */
break;
}
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1e08040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x012a0869);
if (!state->is_cx231xx) {
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1e08040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x012a0869);
}
if (state->is_cx25836)
break;
@ -136,11 +148,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
break;
}
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1809040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x00ec6bd6);
if (!state->is_cx231xx) {
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1809040f);
/* AUX_PLL_FRAC */
cx25840_write4(client, 0x110, 0x00ec6bd6);
}
if (state->is_cx25836)
break;
@ -155,7 +170,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
break;
case 48000:
if (!state->is_cx23885) {
if (!state->is_cx23885 && !state->is_cx231xx) {
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x180a040f);
@ -166,7 +181,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
if (state->is_cx25836)
break;
if (!state->is_cx23885) {
if (!state->is_cx23885 && !state->is_cx231xx) {
/* src1_ctl */
cx25840_write4(client, 0x8f8, 0x08018000);
@ -227,10 +242,9 @@ void cx25840_audio_set_path(struct i2c_client *client)
/* deassert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x00);
if (state->is_cx23885) {
/* Ensure the controller is running when we exit */
/* Ensure the controller is running when we exit */
if (state->is_cx23885 || state->is_cx231xx)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
}
static int get_volume(struct i2c_client *client)

View File

@ -345,6 +345,77 @@ static void cx23885_initialize(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static void cx231xx_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* Internal Reset */
cx25840_and_or(client, 0x102, ~0x01, 0x01);
cx25840_and_or(client, 0x102, ~0x01, 0x00);
/* Stop microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x00);
/* DIF in reset? */
cx25840_write(client, 0x398, 0);
/* Trust the default xtal, no division */
/* This changes for the cx23888 products */
cx25840_write(client, 0x2, 0x76);
/* Bring down the regulator for AUX clk */
cx25840_write(client, 0x1, 0x40);
/* Disable DIF bypass */
cx25840_write4(client, 0x33c, 0x00000001);
/* DIF Src phase inc */
cx25840_write4(client, 0x340, 0x0df7df83);
/* Luma */
cx25840_write4(client, 0x414, 0x00107d12);
/* Chroma */
cx25840_write4(client, 0x420, 0x3d008282);
/* ADC2 input select */
cx25840_write(client, 0x102, 0x10);
/* VIN1 & VIN5 */
cx25840_write(client, 0x103, 0x11);
/* Enable format auto detect */
cx25840_write(client, 0x400, 0);
/* Fast subchroma lock */
/* White crush, Chroma AGC & Chroma Killer enabled */
cx25840_write(client, 0x401, 0xe8);
/* Do the firmware load in a work handler to prevent.
Otherwise the kernel is blocked waiting for the
bit-banging i2c interface to finish uploading the
firmware. */
INIT_WORK(&state->fw_work, cx25840_work_handler);
init_waitqueue_head(&state->fw_wait);
q = create_singlethread_workqueue("cx25840_fw");
prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
queue_work(q, &state->fw_work);
schedule();
finish_wait(&state->fw_wait, &wait);
destroy_workqueue(q);
cx25840_std_setup(client);
/* (re)set input */
set_input(client, state->vid_input, state->aud_input);
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
/* ----------------------------------------------------------------------- */
void cx25840_std_setup(struct i2c_client *client)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
@ -414,39 +485,41 @@ void cx25840_std_setup(struct i2c_client *client)
}
/* DEBUG: Displays configured PLL frequency */
pll_int = cx25840_read(client, 0x108);
pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
pll_post = cx25840_read(client, 0x109);
v4l_dbg(1, cx25840_debug, client,
"PLL regs = int: %u, frac: %u, post: %u\n",
pll_int, pll_frac, pll_post);
if (pll_post) {
int fin, fsc;
int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
pll /= pll_post;
v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
pll / 1000000, pll % 1000000);
v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
pll / 8000000, (pll / 8) % 1000000);
fin = ((u64)src_decimation * pll) >> 12;
if (!state->is_cx231xx) {
pll_int = cx25840_read(client, 0x108);
pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
pll_post = cx25840_read(client, 0x109);
v4l_dbg(1, cx25840_debug, client,
"ADC Sampling freq = %d.%06d MHz\n",
fin / 1000000, fin % 1000000);
"PLL regs = int: %u, frac: %u, post: %u\n",
pll_int, pll_frac, pll_post);
fsc = (((u64)sc) * pll) >> 24L;
v4l_dbg(1, cx25840_debug, client,
"Chroma sub-carrier freq = %d.%06d MHz\n",
fsc / 1000000, fsc % 1000000);
if (pll_post) {
int fin, fsc;
int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
"vblank %i, vactive %i, vblank656 %i, src_dec %i, "
"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
"sc 0x%06x\n",
hblank, hactive, vblank, vactive, vblank656,
src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
pll /= pll_post;
v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
pll / 1000000, pll % 1000000);
v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
pll / 8000000, (pll / 8) % 1000000);
fin = ((u64)src_decimation * pll) >> 12;
v4l_dbg(1, cx25840_debug, client,
"ADC Sampling freq = %d.%06d MHz\n",
fin / 1000000, fin % 1000000);
fsc = (((u64)sc) * pll) >> 24L;
v4l_dbg(1, cx25840_debug, client,
"Chroma sub-carrier freq = %d.%06d MHz\n",
fsc / 1000000, fsc % 1000000);
v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
"vblank %i, vactive %i, vblank656 %i, src_dec %i, "
"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
"sc 0x%06x\n",
hblank, hactive, vblank, vactive, vblank656,
src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
}
}
/* Sets horizontal blanking delay and active lines */
@ -596,7 +669,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
* configuration in reg (for the cx23885) so we have no
* need to attempt to flip bits for earlier av decoders.
*/
if (!state->is_cx23885) {
if (!state->is_cx23885 && !state->is_cx231xx) {
switch (aud_input) {
case CX25840_AUDIO_SERIAL:
/* do nothing, use serial audio input */
@ -619,7 +692,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
if (!state->is_cx23885) {
if (!state->is_cx23885 && !state->is_cx231xx) {
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
@ -653,6 +726,19 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
cx25840_write(client, 0x914, 0xa0);
/* I2S_OUT_CTL:
* I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
* I2S_OUT_MASTER_MODE = Master
*/
cx25840_write(client, 0x918, 0xa0);
cx25840_write(client, 0x919, 0x01);
} else if (state->is_cx231xx) {
/* Audio channel 1 src : Parallel 1 */
cx25840_write(client, 0x124, 0x03);
/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
cx25840_write(client, 0x914, 0xa0);
/* I2S_OUT_CTL:
* I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
* I2S_OUT_MASTER_MODE = Master
@ -1096,7 +1182,7 @@ static void log_audio_status(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
/* This init operation must be called to load the driver's firmware.
/* This load_fw operation must be called to load the driver's firmware.
Without this the audio standard detection will fail and you will
only get mono.
@ -1106,18 +1192,20 @@ static void log_audio_status(struct i2c_client *client)
postponing it is that loading this firmware takes a long time (seconds)
due to the slow i2c bus speed. So it will speed up the boot process if
you can avoid loading the fw as long as the video device isn't used. */
static int cx25840_init(struct v4l2_subdev *sd, u32 val)
static int cx25840_load_fw(struct v4l2_subdev *sd)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (!state->is_initialized) {
/* initialize on first use */
/* initialize and load firmware */
state->is_initialized = 1;
if (state->is_cx25836)
cx25836_initialize(client);
else if (state->is_cx23885)
cx23885_initialize(client);
else if (state->is_cx231xx)
cx231xx_initialize(client);
else
cx25840_initialize(client);
}
@ -1159,7 +1247,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
v4l_dbg(1, cx25840_debug, client, "%s output\n",
enable ? "enable" : "disable");
if (enable) {
if (state->is_cx23885) {
if (state->is_cx23885 || state->is_cx231xx) {
u8 v = (cx25840_read(client, 0x421) | 0x0b);
cx25840_write(client, 0x421, v);
} else {
@ -1169,7 +1257,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
state->is_cx25836 ? 0x04 : 0x07);
}
} else {
if (state->is_cx23885) {
if (state->is_cx23885 || state->is_cx231xx) {
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
} else {
@ -1234,22 +1322,24 @@ static int cx25840_s_radio(struct v4l2_subdev *sd)
return 0;
}
static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int cx25840_s_video_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
return set_input(client, route->input, state->aud_input);
return set_input(client, input, state->aud_input);
}
static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
static int cx25840_s_audio_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (state->is_cx25836)
return -EINVAL;
return set_input(client, state->vid_input, route->input);
return set_input(client, state->vid_input, input);
}
static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
@ -1350,6 +1440,8 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
cx25836_initialize(client);
else if (state->is_cx23885)
cx23885_initialize(client);
else if (state->is_cx231xx)
cx231xx_initialize(client);
else
cx25840_initialize(client);
return 0;
@ -1382,8 +1474,9 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.g_ctrl = cx25840_g_ctrl,
.s_ctrl = cx25840_s_ctrl,
.queryctrl = cx25840_queryctrl,
.s_std = cx25840_s_std,
.reset = cx25840_reset,
.init = cx25840_init,
.load_fw = cx25840_load_fw,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
@ -1392,7 +1485,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
.s_frequency = cx25840_s_frequency,
.s_std = cx25840_s_std,
.s_radio = cx25840_s_radio,
.g_tuner = cx25840_g_tuner,
.s_tuner = cx25840_s_tuner,
@ -1449,6 +1541,8 @@ static int cx25840_probe(struct i2c_client *client,
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
} else if (device_id == 0x1313) {
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
} else if ((device_id & 0xfff0) == 0x5A30) {
id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
}
else {
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
@ -1471,6 +1565,7 @@ static int cx25840_probe(struct i2c_client *client,
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
state->is_cx231xx = (device_id == 0x5a3e);
state->vid_input = CX25840_COMPOSITE7;
state->aud_input = CX25840_AUDIO8;
state->audclk_freq = 48000;

View File

@ -50,6 +50,7 @@ struct cx25840_state {
u32 rev;
int is_cx25836;
int is_cx23885;
int is_cx231xx;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */

View File

@ -25,6 +25,7 @@
#define FWFILE "v4l-cx25840.fw"
#define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
#define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw"
/*
* Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
@ -96,9 +97,17 @@ int cx25840_loadfw(struct i2c_client *client)
u8 buffer[FWSEND];
const u8 *ptr;
int size, retval;
int MAX_BUF_SIZE = FWSEND;
if (state->is_cx23885)
firmware = FWFILE_CX23885;
else if (state->is_cx231xx)
firmware = FWFILE_CX231XX;
if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) {
v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
}
if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
v4l_err(client, "unable to open firmware %s\n", firmware);
@ -113,7 +122,7 @@ int cx25840_loadfw(struct i2c_client *client)
size = fw->size;
ptr = fw->data;
while (size > 0) {
int len = min(FWSEND - 2, size);
int len = min(MAX_BUF_SIZE - 2, size);
memcpy(buffer + 2, ptr, len);

View File

@ -3049,7 +3049,7 @@ static void cx88_card_setup(struct cx88_core *core)
ctl.fname);
call_all(core, tuner, s_config, &xc2028_cfg);
}
call_all(core, core, s_standby, 0);
call_all(core, tuner, s_standby);
}
/* ------------------------------------------------------------------ */
@ -3221,16 +3221,19 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
The radio_type is sometimes missing, or set to UNSET but
later code configures a tea5767.
*/
v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner", "tuner",
v4l2_i2c_new_probed_subdev(&core->v4l2_dev, &core->i2c_adap,
"tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_RADIO));
if (has_demod)
v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner",
"tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
v4l2_i2c_new_probed_subdev(&core->v4l2_dev,
&core->i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
if (core->board.tuner_addr == ADDR_UNSET) {
v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner",
"tuner", has_demod ? tv_addrs + 4 : tv_addrs);
v4l2_i2c_new_probed_subdev(&core->v4l2_dev,
&core->i2c_adap, "tuner", "tuner",
has_demod ? tv_addrs + 4 : tv_addrs);
} else {
v4l2_i2c_new_subdev(&core->i2c_adap,
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
"tuner", "tuner", core->board.tuner_addr);
}
}

View File

@ -991,7 +991,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
set_tvaudio(core);
// tell i2c chips
call_all(core, tuner, s_std, norm);
call_all(core, core, s_std, norm);
// done
return 0;

View File

@ -1168,7 +1168,7 @@ static int dvb_register(struct cx8802_dev *dev)
fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
/* Put the analog decoder in standby to keep it quiet */
call_all(core, core, s_standby, 0);
call_all(core, tuner, s_standby);
/* register everything */
return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,

View File

@ -428,10 +428,8 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input)
routes for different inputs. HVR-1300 surely does */
if (core->board.audio_chip &&
core->board.audio_chip == V4L2_IDENT_WM8775) {
struct v4l2_routing route;
route.input = INPUT(input).audioroute;
call_all(core, audio, s_routing, &route);
call_all(core, audio, s_routing,
INPUT(input).audioroute, 0, 0);
}
/* cx2388's C-ADC is connected to the tuner only.
When used with S-Video, that ADC is busy dealing with
@ -823,10 +821,8 @@ static int video_open(struct file *file)
if (core->board.radio.audioroute) {
if(core->board.audio_chip &&
core->board.audio_chip == V4L2_IDENT_WM8775) {
struct v4l2_routing route;
route.input = core->board.radio.audioroute;
call_all(core, audio, s_routing, &route);
call_all(core, audio, s_routing,
core->board.radio.audioroute, 0, 0);
}
/* "I2S ADC mode" */
core->tvaudio = WW_I2SADC;
@ -931,7 +927,7 @@ static int video_release(struct file *file)
kfree(fh);
if(atomic_dec_and_test(&dev->core->users))
call_all(dev->core, core, s_standby, 0);
call_all(dev->core, tuner, s_standby);
return 0;
}
@ -1882,18 +1878,15 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
/* load and configure helper modules */
if (core->board.audio_chip == V4L2_IDENT_WM8775)
v4l2_i2c_new_subdev(&core->i2c_adap,
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
"wm8775", "wm8775", 0x36 >> 1);
if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
/* This probes for a tda9874 as is used on some
Pixelview Ultra boards. */
static const unsigned short i2c_addr[] = {
0xb0 >> 1, I2C_CLIENT_END
};
v4l2_i2c_new_probed_subdev(&core->i2c_adap,
"tvaudio", "tvaudio", i2c_addr);
v4l2_i2c_new_probed_subdev_addr(&core->v4l2_dev,
&core->i2c_adap,
"tvaudio", "tvaudio", 0xb0 >> 1);
}
switch (core->boardnr) {

View File

@ -41,7 +41,7 @@
#include <linux/version.h>
#include <linux/mutex.h>
#define CX88_VERSION_CODE KERNEL_VERSION(0,0,6)
#define CX88_VERSION_CODE KERNEL_VERSION(0,0,7)
#define UNSET (-1U)

View File

@ -31,6 +31,8 @@
#include <media/msp3400.h>
#include <media/saa7115.h>
#include <media/tvp5150.h>
#include <media/tvaudio.h>
#include <media/i2c-addr.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
@ -1240,6 +1242,7 @@ struct em28xx_board em28xx_boards[] = {
[EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
.name = "Compro VideoMate ForYou/Stereo",
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tvaudio_addr = 0xb0,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
.adecoder = EM28XX_TVAUDIO,
@ -1444,6 +1447,24 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = {
{0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
};
/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
static unsigned short saa711x_addrs[] = {
0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
I2C_CLIENT_END };
static unsigned short tvp5150_addrs[] = {
0xb8 >> 1,
0xba >> 1,
I2C_CLIENT_END
};
static unsigned short msp3400_addrs[] = {
0x80 >> 1,
0x88 >> 1,
I2C_CLIENT_END
};
int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
{
int rc = 0;
@ -1672,31 +1693,55 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
}
}
static void em28xx_config_tuner(struct em28xx *dev)
static void em28xx_tuner_setup(struct em28xx *dev)
{
struct v4l2_priv_tun_config xc2028_cfg;
struct tuner_setup tun_setup;
struct v4l2_frequency f;
if (dev->tuner_type == TUNER_ABSENT)
return;
memset(&tun_setup, 0, sizeof(tun_setup));
tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
tun_setup.type = dev->tuner_type;
tun_setup.addr = dev->tuner_addr;
tun_setup.tuner_callback = em28xx_tuner_callback;
em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
if (dev->board.radio.type) {
tun_setup.type = dev->board.radio.type;
tun_setup.addr = dev->board.radio_addr;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
}
if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
tun_setup.type = dev->tuner_type;
tun_setup.addr = dev->tuner_addr;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
}
if (dev->tda9887_conf) {
struct v4l2_priv_tun_config tda9887_cfg;
tda9887_cfg.tuner = TUNER_TDA9887;
tda9887_cfg.priv = &dev->tda9887_conf;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
}
if (dev->tuner_type == TUNER_XC2028) {
struct v4l2_priv_tun_config xc2028_cfg;
struct xc2028_ctrl ctl;
memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
memset(&ctl, 0, sizeof(ctl));
em28xx_setup_xc3028(dev, &ctl);
xc2028_cfg.tuner = TUNER_XC2028;
xc2028_cfg.priv = &ctl;
em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
}
/* configure tuner */
@ -1704,7 +1749,7 @@ static void em28xx_config_tuner(struct em28xx *dev)
f.type = V4L2_TUNER_ANALOG_TV;
f.frequency = 9076; /* just a magic number */
dev->ctl_freq = f.frequency;
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
}
static int em28xx_hint_board(struct em28xx *dev)
@ -1911,22 +1956,52 @@ void em28xx_card_setup(struct em28xx *dev)
if (tuner >= 0)
dev->tuner_type = tuner;
#ifdef CONFIG_MODULES
/* request some modules */
if (dev->board.has_msp34xx)
request_module("msp3400");
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"msp3400", "msp3400", msp3400_addrs);
if (dev->board.decoder == EM28XX_SAA711X)
request_module("saa7115");
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"saa7115", "saa7115_auto", saa711x_addrs);
if (dev->board.decoder == EM28XX_TVP5150)
request_module("tvp5150");
if (dev->board.tuner_type != TUNER_ABSENT)
request_module("tuner");
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvp5150", "tvp5150", tvp5150_addrs);
if (dev->board.adecoder == EM28XX_TVAUDIO)
request_module("tvaudio");
#endif
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvaudio", "tvaudio", dev->board.tvaudio_addr);
em28xx_config_tuner(dev);
if (dev->board.tuner_type != TUNER_ABSENT) {
int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
if (dev->board.radio.type)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", "tuner", dev->board.radio_addr);
if (has_demod)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
if (dev->tuner_addr == 0) {
enum v4l2_i2c_tuner_type type =
has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
struct v4l2_subdev *sd;
sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(type));
if (sd)
dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
} else {
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", "tuner", dev->tuner_addr);
}
}
em28xx_tuner_setup(dev);
em28xx_ir_init(dev);
}
@ -1975,6 +2050,9 @@ void em28xx_release_resources(struct em28xx *dev)
em28xx_remove_from_devlist(dev);
em28xx_i2c_unregister(dev);
v4l2_device_unregister(&dev->v4l2_dev);
usb_put_dev(dev->udev);
/* Mark device as unused */
@ -1986,6 +2064,7 @@ void em28xx_release_resources(struct em28xx *dev)
* allocates and inits the device structs, registers i2c bus and v4l device
*/
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
struct usb_interface *interface,
int minor)
{
struct em28xx *dev = *devhandle;
@ -2019,9 +2098,16 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
}
}
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval < 0) {
em28xx_errdev("Call to v4l2_device_register() failed!\n");
return retval;
}
/* register i2c bus */
errCode = em28xx_i2c_register(dev);
if (errCode < 0) {
v4l2_device_unregister(&dev->v4l2_dev);
em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
__func__, errCode);
return errCode;
@ -2033,6 +2119,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
/* Configure audio */
errCode = em28xx_audio_setup(dev);
if (errCode < 0) {
v4l2_device_unregister(&dev->v4l2_dev);
em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
__func__, errCode);
}
@ -2077,7 +2164,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
em28xx_init_extension(dev);
/* Save some power by putting tuner to sleep */
em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
return 0;
@ -2096,7 +2183,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
struct usb_device *udev;
struct usb_interface *uif;
struct em28xx *dev = NULL;
int retval = -ENODEV;
int retval;
int i, nr, ifnum, isoc_pipe;
char *speed;
char descr[255] = "";
@ -2118,7 +2205,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
interface->altsetting[0].desc.bInterfaceClass);
em28xx_devused &= ~(1<<nr);
return -ENODEV;
retval = -ENODEV;
goto err;
}
endpoint = &interface->cur_altsetting->endpoint[0].desc;
@ -2151,7 +2239,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
"interface not used by the driver\n");
em28xx_devused &= ~(1<<nr);
return -ENODEV;
retval = -ENODEV;
goto err;
}
}
@ -2194,7 +2283,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
EM28XX_MAXBOARDS);
em28xx_devused &= ~(1<<nr);
return -ENOMEM;
retval = -ENOMEM;
goto err;
}
/* allocate memory for our device state and initialize it */
@ -2202,7 +2292,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (dev == NULL) {
em28xx_err(DRIVER_NAME ": out of memory!\n");
em28xx_devused &= ~(1<<nr);
return -ENOMEM;
retval = -ENOMEM;
goto err;
}
snprintf(dev->name, 29, "em28xx #%d", nr);
@ -2229,7 +2320,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
em28xx_errdev("out of memory!\n");
em28xx_devused &= ~(1<<nr);
kfree(dev);
return -ENOMEM;
retval = -ENOMEM;
goto err;
}
for (i = 0; i < dev->num_alt ; i++) {
@ -2244,12 +2336,11 @@ static int em28xx_usb_probe(struct usb_interface *interface,
/* allocate device struct */
mutex_init(&dev->lock);
mutex_lock(&dev->lock);
retval = em28xx_init_dev(&dev, udev, nr);
retval = em28xx_init_dev(&dev, udev, interface, nr);
if (retval) {
em28xx_devused &= ~(1<<dev->devno);
kfree(dev);
return retval;
goto err;
}
/* save our data pointer in this interface device */
@ -2263,6 +2354,9 @@ static int em28xx_usb_probe(struct usb_interface *interface,
mutex_unlock(&dev->lock);
return 0;
err:
return retval;
}
/*
@ -2288,6 +2382,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
wake_up_interruptible_all(&dev->open);
v4l2_device_disconnect(&dev->v4l2_dev);
if (dev->users) {
em28xx_warn
("device /dev/video%d is open! Deregistration and memory "

View File

@ -1018,14 +1018,10 @@ EXPORT_SYMBOL_GPL(em28xx_init_isoc);
*/
void em28xx_wake_i2c(struct em28xx *dev)
{
struct v4l2_routing route;
int zero = 0;
route.input = INPUT(dev->ctl_input)->vmux;
route.output = 0;
em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
INPUT(dev->ctl_input)->vmux, 0, 0);
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
}
/*

Some files were not shown because too many files have changed in this diff Show More