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:
Коммит
4ef4327b30
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
|
@ -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:
|
|
@ -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)
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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_ */
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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__ */
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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__);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
@ -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);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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);
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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, ¶ms);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
||||
}
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче