* '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
...
--- /dev/null
+ 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>
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.
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.
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
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
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
\ No newline at end of file
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,
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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:
_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)
#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__ */
--- /dev/null
+/*
+ * 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_ */
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
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
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 */
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:
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;
}
/* 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);
.i2c_algo = &af9015_i2c_algo,
- .num_device_descs = 7,
+ .num_device_descs = 9,
.devices = {
{
.name = "Xtensions XD-380",
.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},
},
}
#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] */
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 */
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
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);
.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},
+ },
}
};
#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
#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
#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
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));
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
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
}
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);
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");
}
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;
}
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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
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>
#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 */
-#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"
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;
mutex_unlock(&radio->lock);
video_unregister_device(&radio->videodev);
+ v4l2_device_disconnect(&radio->v4l2_dev);
}
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;
}
{
struct dsbr100_device *radio = videodev_to_radio(videodev);
+ v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio->transfer_buffer);
kfree(radio);
}
.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;
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;
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,
};
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,
};
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,
};
/*
* 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,
};
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,
};
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,
};
* 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)
#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 */
/* 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);
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);
/* 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 */
usb_set_intfdata(intf, NULL);
video_unregister_device(radio->videodev);
+ v4l2_device_disconnect(&radio->v4l2_dev);
}
/* vidioc_querycap - query device capabilities */
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;
}
.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");
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) {
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;
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;
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,
};
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,
};
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,
};
/* 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]);
/* 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",
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
- goto err_all;
+ goto err_video;
}
/* rds buffer configuration */
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:
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,
};
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,
};
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,
};
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,
};
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
source "drivers/media/video/em28xx/Kconfig"
+source "drivers/media/video/cx231xx/Kconfig"
+
source "drivers/media/video/usbvision/Kconfig"
source "drivers/media/video/usbvideo/Kconfig"
This driver can be compiled as a module, called s2255drv.
endif # V4L_USB_DRIVERS
-
endif # VIDEO_CAPTURE_DRIVERS
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)
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
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
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 */
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;
}
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);
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;
}
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
.name = "Hauppauge HVR850",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
+ .i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
.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,
.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,
},
};
/* 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");
}
/* 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");
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
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);
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__);
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)
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)
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)
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)
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);
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;
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++;
/* 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;
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;
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]);
}
#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
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;
}
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);
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;
}
}
- 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;
}
char *name;
unsigned int tuner_type;
unsigned char tuner_addr;
+ unsigned char i2c_clk_divider;
struct au0828_input input[AU0828_MAX_INPUT];
};
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);
.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,
};
static const struct v4l2_subdev_ops bt819_ops = {
.core = &bt819_core_ops,
- .tuner = &bt819_tuner_ops,
.video = &bt819_video_ops,
};
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);
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 */
val = encoder->reg[0xdc];
- if (route->input == 0)
+ if (input == 0)
val |= 0x40; /* CBSWAP */
else
val &= ~0x40; /* !CBSWAP */
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:
/* 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;
/* 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;
};
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);
}
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;
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;
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. */
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;
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
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:
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:
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;
}
break;
}
id = tvnorm->v4l2_id;
- bttv_call_all(btv, tuner, s_std, id);
+ bttv_call_all(btv, core, s_std, id);
return 0;
}
#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>
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;
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;
}
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;
}
int cx18_audio_set_io(struct cx18 *cx)
{
const struct cx18_card_audio_input *in;
- struct v4l2_routing route;
u32 val;
int err;
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;
static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
{
- 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.
- */
+ /*
+ * 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 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);
+ /* 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);
+ /* 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;
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+ return 0;
+}
- case CX18_AV_INIT_NORMAL:
- default:
- if (!state->is_initialized) {
- /* initialize on first use */
- state->is_initialized = 1;
- cx18_av_initialize(cx);
- }
- break;
+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);
+
+ if (!state->is_initialized) {
+ /* initialize on first use */
+ state->is_initialized = 1;
+ cx18_av_initialize(cx);
}
return 0;
}
}
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)
.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,
.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 = {
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);
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) {
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;
/* 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) {
}
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;
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,
};
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;
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;
(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;
}
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;
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);
}
--- /dev/null
+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.
--- /dev/null
+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
+
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ cx231xx_avcore.c - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+
+ This program contains the specific code to control the avdecoder chip and
+ other related usb control functions for cx231xx based chipset.
+
+ 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 "cx231xx.h"
+
+/******************************************************************************
+ -: BLOCK ARRANGEMENT :-
+ I2S block ----------------------|
+ [I2S audio] |
+ |
+ Analog Front End --> Direct IF -|-> Cx25840 --> Audio
+ [video & audio] | [Audio]
+ |
+ |-> Cx25840 --> Video
+ [Video]
+
+*******************************************************************************/
+
+/******************************************************************************
+ * A F E - B L O C K C O N T R O L functions *
+ * [ANALOG FRONT END] *
+ ******************************************************************************/
+static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
+{
+ return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ saddr, 2, data, 1);
+}
+
+static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
+{
+ int status;
+ u32 temp = 0;
+
+ status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ saddr, 2, &temp, 1);
+ *data = (u8) temp;
+ return status;
+}
+
+int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count)
+{
+ int status = 0;
+ u8 temp = 0;
+ u8 afe_power_status = 0;
+ int i = 0;
+
+ /* super block initialize */
+ temp = (u8) (ref_count & 0xff);
+ status = afe_write_byte(dev, SUP_BLK_TUNE2, temp);
+ if (status < 0)
+ return status;
+
+ status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status);
+ if (status < 0)
+ return status;
+
+ temp = (u8) ((ref_count & 0x300) >> 8);
+ temp |= 0x40;
+ status = afe_write_byte(dev, SUP_BLK_TUNE1, temp);
+ if (status < 0)
+ return status;
+
+ status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f);
+ if (status < 0)
+ return status;
+
+ /* enable pll */
+ while (afe_power_status != 0x18) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18);
+ if (status < 0) {
+ cx231xx_info(
+ ": Init Super Block failed in send cmd\n");
+ break;
+ }
+
+ status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status);
+ afe_power_status &= 0xff;
+ if (status < 0) {
+ cx231xx_info(
+ ": Init Super Block failed in receive cmd\n");
+ break;
+ }
+ i++;
+ if (i == 10) {
+ cx231xx_info(
+ ": Init Super Block force break in loop !!!!\n");
+ status = -1;
+ break;
+ }
+ }
+
+ if (status < 0)
+ return status;
+
+ /* start tuning filter */
+ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40);
+ if (status < 0)
+ return status;
+
+ msleep(5);
+
+ /* exit tuning */
+ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00);
+
+ return status;
+}
+
+int cx231xx_afe_init_channels(struct cx231xx *dev)
+{
+ int status = 0;
+
+ /* power up all 3 channels, clear pd_buffer */
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00);
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00);
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00);
+
+ /* Enable quantizer calibration */
+ status = afe_write_byte(dev, ADC_COM_QUANT, 0x02);
+
+ /* channel initialize, force modulator (fb) reset */
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17);
+
+ /* start quantilizer calibration */
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10);
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10);
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10);
+ msleep(5);
+
+ /* exit modulator (fb) reset */
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07);
+
+ /* enable the pre_clamp in each channel for single-ended input */
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0);
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0);
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0);
+
+ /* use diode instead of resistor, so set term_en to 0, res_en to 0 */
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
+ ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00);
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
+ ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00);
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
+ ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00);
+
+ /* dynamic element matching off */
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03);
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03);
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03);
+
+ return status;
+}
+
+int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev)
+{
+ u8 c_value = 0;
+ int status = 0;
+
+ status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value);
+ c_value &= (~(0x50));
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value);
+
+ return status;
+}
+
+/*
+ The Analog Front End in Cx231xx has 3 channels. These
+ channels are used to share between different inputs
+ like tuner, s-video and composite inputs.
+
+ channel 1 ----- pin 1 to pin4(in reg is 1-4)
+ channel 2 ----- pin 5 to pin8(in reg is 5-8)
+ channel 3 ----- pin 9 to pin 12(in reg is 9-11)
+*/
+int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux)
+{
+ u8 ch1_setting = (u8) input_mux;
+ u8 ch2_setting = (u8) (input_mux >> 8);
+ u8 ch3_setting = (u8) (input_mux >> 16);
+ int status = 0;
+ u8 value = 0;
+
+ if (ch1_setting != 0) {
+ status = afe_read_byte(dev, ADC_INPUT_CH1, &value);
+ value &= (!INPUT_SEL_MASK);
+ value |= (ch1_setting - 1) << 4;
+ value &= 0xff;
+ status = afe_write_byte(dev, ADC_INPUT_CH1, value);
+ }
+
+ if (ch2_setting != 0) {
+ status = afe_read_byte(dev, ADC_INPUT_CH2, &value);
+ value &= (!INPUT_SEL_MASK);
+ value |= (ch2_setting - 1) << 4;
+ value &= 0xff;
+ status = afe_write_byte(dev, ADC_INPUT_CH2, value);
+ }
+
+ /* For ch3_setting, the value to put in the register is
+ 7 less than the input number */
+ if (ch3_setting != 0) {
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
+ value &= (!INPUT_SEL_MASK);
+ value |= (ch3_setting - 1) << 4;
+ value &= 0xff;
+ status = afe_write_byte(dev, ADC_INPUT_CH3, value);
+ }
+
+ return status;
+}
+
+int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode)
+{
+ int status = 0;
+
+ /*
+ * FIXME: We need to implement the AFE code for LOW IF and for HI IF.
+ * Currently, only baseband works.
+ */
+
+ switch (mode) {
+ case AFE_MODE_LOW_IF:
+ /* SetupAFEforLowIF(); */
+ break;
+ case AFE_MODE_BASEBAND:
+ status = cx231xx_afe_setup_AFE_for_baseband(dev);
+ break;
+ case AFE_MODE_EU_HI_IF:
+ /* SetupAFEforEuHiIF(); */
+ break;
+ case AFE_MODE_US_HI_IF:
+ /* SetupAFEforUsHiIF(); */
+ break;
+ case AFE_MODE_JAPAN_HI_IF:
+ /* SetupAFEforJapanHiIF(); */
+ break;
+ }
+
+ if ((mode != dev->afe_mode) &&
+ (dev->video_input == CX231XX_VMUX_TELEVISION))
+ status = cx231xx_afe_adjust_ref_count(dev,
+ CX231XX_VMUX_TELEVISION);
+
+ dev->afe_mode = mode;
+
+ return status;
+}
+
+int cx231xx_afe_update_power_control(struct cx231xx *dev,
+ enum AV_MODE avmode)
+{
+ u8 afe_power_status = 0;
+ int status = 0;
+
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x00);
+ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x70);
+
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
+ FLD_PWRDN_PD_BIAS |
+ FLD_PWRDN_PD_TUNECK;
+ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
+ afe_power_status);
+ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x00);
+ } else {
+ cx231xx_info("Invalid AV mode input\n");
+ status = -1;
+ }
+ break;
+ default:
+ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x40);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x40);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x00);
+ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x70);
+
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
+ FLD_PWRDN_PD_BIAS |
+ FLD_PWRDN_PD_TUNECK;
+ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
+ afe_power_status);
+ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x40);
+ } else {
+ cx231xx_info("Invalid AV mode input\n");
+ status = -1;
+ }
+ } /* switch */
+
+ return status;
+}
+
+int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input)
+{
+ u8 input_mode = 0;
+ u8 ntf_mode = 0;
+ int status = 0;
+
+ dev->video_input = video_input;
+
+ if (video_input == CX231XX_VMUX_TELEVISION) {
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode);
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3,
+ &ntf_mode);
+ } else {
+ status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode);
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1,
+ &ntf_mode);
+ }
+
+ input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1);
+
+ switch (input_mode) {
+ case SINGLE_ENDED:
+ dev->afe_ref_count = 0x23C;
+ break;
+ case LOW_IF:
+ dev->afe_ref_count = 0x24C;
+ break;
+ case EU_IF:
+ dev->afe_ref_count = 0x258;
+ break;
+ case US_IF:
+ dev->afe_ref_count = 0x260;
+ break;
+ default:
+ break;
+ }
+
+ status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count);
+
+ return status;
+}
+
+/******************************************************************************
+ * V I D E O / A U D I O D E C O D E R C O N T R O L functions *
+ ******************************************************************************/
+static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
+{
+ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, data, 1);
+}
+
+static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
+{
+ int status;
+ u32 temp = 0;
+
+ status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, &temp, 1);
+ *data = (u8) temp;
+ return status;
+}
+
+static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data)
+{
+ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, data, 4);
+}
+
+static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data)
+{
+ return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, data, 4);
+}
+
+int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input)
+{
+ int status = 0;
+
+ switch (INPUT(input)->type) {
+ case CX231XX_VMUX_COMPOSITE1:
+ case CX231XX_VMUX_SVIDEO:
+ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
+ (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) {
+ /* External AV */
+ status = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ENXTERNAL_AV);
+ if (status < 0) {
+ cx231xx_errdev("%s: set_power_mode : Failed to"
+ " set Power - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+ }
+ status = cx231xx_set_decoder_video_input(dev,
+ INPUT(input)->type,
+ INPUT(input)->vmux);
+ break;
+ case CX231XX_VMUX_TELEVISION:
+ case CX231XX_VMUX_CABLE:
+ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
+ (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) {
+ /* Tuner */
+ status = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ANALOGT_TV);
+ if (status < 0) {
+ cx231xx_errdev("%s: set_power_mode:Failed"
+ " to set Power - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+ }
+ status = cx231xx_set_decoder_video_input(dev,
+ CX231XX_VMUX_COMPOSITE1,
+ INPUT(input)->vmux);
+ break;
+ default:
+ cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n",
+ __func__, INPUT(input)->type);
+ break;
+ }
+
+ /* save the selection */
+ dev->video_input = input;
+
+ return status;
+}
+
+int cx231xx_set_decoder_video_input(struct cx231xx *dev,
+ u8 pin_type, u8 input)
+{
+ int status = 0;
+ u32 value = 0;
+
+ if (pin_type != dev->video_input) {
+ status = cx231xx_afe_adjust_ref_count(dev, pin_type);
+ if (status < 0) {
+ cx231xx_errdev("%s: adjust_ref_count :Failed to set"
+ "AFE input mux - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+ }
+
+ /* call afe block to set video inputs */
+ status = cx231xx_afe_set_input_mux(dev, input);
+ if (status < 0) {
+ cx231xx_errdev("%s: set_input_mux :Failed to set"
+ " AFE input mux - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ switch (pin_type) {
+ case CX231XX_VMUX_COMPOSITE1:
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+ value |= (0 << 13) | (1 << 4);
+ value &= ~(1 << 5);
+
+ /* set [24:23] [22:15] to 0 */
+ value &= (~(0x1ff8000));
+ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
+ value |= 0x1000000;
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
+ value |= (1 << 7);
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
+
+ /* Set vip 1.1 output mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1,
+ FLD_OUT_MODE,
+ OUT_MODE_VIP11);
+
+ /* Tell DIF object to go to baseband mode */
+ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* enable the VBI_GATE_EN */
+ value |= FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable */
+ value |= FLD_VGA_AUTO_EN;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set CVBS input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0));
+ break;
+ case CX231XX_VMUX_SVIDEO:
+ /* Disable the use of DIF */
+
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+
+ /* set [24:23] [22:15] to 0 */
+ value &= (~(0x1ff8000));
+ /* set FUNC_MODE[24:23] = 2
+ IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */
+ value |= 0x1000010;
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ /* Tell DIF object to go to baseband mode */
+ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* enable the VBI_GATE_EN */
+ value |= FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable */
+ value |= FLD_VGA_AUTO_EN;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set YC input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL,
+ FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1));
+
+ /* Chroma to ADC2 */
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+ value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */
+
+ /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8)
+ This sets them to use video
+ rather than audio. Only one of the two will be in use. */
+ value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3);
+
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND);
+ break;
+ case CX231XX_VMUX_TELEVISION:
+ case CX231XX_VMUX_CABLE:
+ default:
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ /* Disable the use of DIF */
+
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+ value |= (0 << 13) | (1 << 4);
+ value &= ~(1 << 5);
+
+ /* set [24:23] [22:15] to 0 */
+ value &= (~(0x1FF8000));
+ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
+ value |= 0x1000000;
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
+ value |= (1 << 7);
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
+
+ /* Set vip 1.1 output mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1, FLD_OUT_MODE,
+ OUT_MODE_VIP11);
+
+ /* Tell DIF object to go to baseband mode */
+ status = cx231xx_dif_set_standard(dev,
+ DIF_USE_BASEBAND);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* enable the VBI_GATE_EN */
+ value |= FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable */
+ value |= FLD_VGA_AUTO_EN;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set CVBS input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE,
+ INPUT_MODE_CVBS_0));
+ break;
+ default:
+ /* Enable the DIF for the tuner */
+
+ /* Reinitialize the DIF */
+ status = cx231xx_dif_set_standard(dev, dev->norm);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Make sure bypass is cleared */
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value);
+
+ /* Clear the bypass bit */
+ value &= ~FLD_DIF_DIF_BYPASS;
+
+ /* Enable the use of the DIF block */
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL, value);
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* Disable the VBI_GATE_EN */
+ value &= ~FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable, AGC, and
+ set the skip count to 2 */
+ value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Wait until AGC locks up */
+ msleep(1);
+
+ /* Disable the auto-VGA enable AGC */
+ value &= ~(FLD_VGA_AUTO_EN);
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Enable Polaris B0 AGC output */
+ status = vid_blk_read_word(dev, PIN_CTRL, &value);
+ value |= (FLD_OEF_AGC_RF) |
+ (FLD_OEF_AGC_IFVGA) |
+ (FLD_OEF_AGC_IF);
+ status = vid_blk_write_word(dev, PIN_CTRL, value);
+
+ /* Set vip 1.1 output mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1, FLD_OUT_MODE,
+ OUT_MODE_VIP11);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set CVBS input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE,
+ INPUT_MODE_CVBS_0));
+
+ /* Set some bits in AFE_CTRL so that channel 2 or 3
+ * is ready to receive audio */
+ /* Clear clamp for channels 2 and 3 (bit 16-17) */
+ /* Clear droop comp (bit 19-20) */
+ /* Set VGA_SEL (for audio control) (bit 7-8) */
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+
+ value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2;
+
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+ break;
+
+ }
+ break;
+ }
+
+ /* Set raw VBI mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1, FLD_VBIHACTRAW_EN,
+ cx231xx_set_field(FLD_VBIHACTRAW_EN, 1));
+
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
+ if (value & 0x02) {
+ value |= (1 << 19);
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
+ }
+
+ return status;
+}
+
+/*
+ * Handle any video-mode specific overrides that are different
+ * on a per video standards basis after touching the MODE_CTRL
+ * register which resets many values for autodetect
+ */
+int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev)
+{
+ int status = 0;
+
+ cx231xx_info("do_mode_ctrl_overrides : 0x%x\n",
+ (unsigned int)dev->norm);
+
+ /* Change the DFE_CTRL3 bp_percent to fix flagging */
+ status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280);
+
+ if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) {
+ cx231xx_info("do_mode_ctrl_overrides NTSC\n");
+
+ /* Move the close caption lines out of active video,
+ adjust the active video start point */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VBLANK_CNT, 0x18);
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VACTIVE_CNT,
+ 0x1E6000);
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_V656BLANK_CNT,
+ 0x1E000000);
+
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ HORIZ_TIM_CTRL,
+ FLD_HBLANK_CNT,
+ cx231xx_set_field
+ (FLD_HBLANK_CNT, 0x79));
+ } else if (dev->norm & V4L2_STD_SECAM) {
+ cx231xx_info("do_mode_ctrl_overrides SECAM\n");
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VBLANK_CNT, 0x24);
+ /* Adjust the active video horizontal start point */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ HORIZ_TIM_CTRL,
+ FLD_HBLANK_CNT,
+ cx231xx_set_field
+ (FLD_HBLANK_CNT, 0x85));
+ } else {
+ cx231xx_info("do_mode_ctrl_overrides PAL\n");
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VBLANK_CNT, 0x24);
+ /* Adjust the active video horizontal start point */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ HORIZ_TIM_CTRL,
+ FLD_HBLANK_CNT,
+ cx231xx_set_field
+ (FLD_HBLANK_CNT, 0x85));
+ }
+
+ return status;
+}
+
+int cx231xx_set_audio_input(struct cx231xx *dev, u8 input)
+{
+ int status = 0;
+ enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE;
+
+ switch (INPUT(input)->amux) {
+ case CX231XX_AMUX_VIDEO:
+ ainput = AUDIO_INPUT_TUNER_TV;
+ break;
+ case CX231XX_AMUX_LINE_IN:
+ status = cx231xx_i2s_blk_set_audio_input(dev, input);
+ ainput = AUDIO_INPUT_LINE;
+ break;
+ default:
+ break;
+ }
+
+ status = cx231xx_set_audio_decoder_input(dev, ainput);
+
+ return status;
+}
+
+int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
+ enum AUDIO_INPUT audio_input)
+{
+ u32 dwval;
+ int status;
+ u8 gen_ctrl;
+ u32 value = 0;
+
+ /* Put it in soft reset */
+ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
+ gen_ctrl |= 1;
+ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
+
+ switch (audio_input) {
+ case AUDIO_INPUT_LINE:
+ /* setup AUD_IO control from Merlin paralle output */
+ value = cx231xx_set_field(FLD_AUD_CHAN1_SRC,
+ AUD_CHAN_SRC_PARALLEL);
+ status = vid_blk_write_word(dev, AUD_IO_CTRL, value);
+
+ /* setup input to Merlin, SRC2 connect to AC97
+ bypass upsample-by-2, slave mode, sony mode, left justify
+ adr 091c, dat 01000000 */
+ status = vid_blk_read_word(dev, AC97_CTL, &dwval);
+
+ status = vid_blk_write_word(dev, AC97_CTL,
+ (dwval | FLD_AC97_UP2X_BYPASS));
+
+ /* select the parallel1 and SRC3 */
+ status = vid_blk_write_word(dev, BAND_OUT_SEL,
+ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) |
+ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) |
+ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0));
+
+ /* unmute all, AC97 in, independence mode
+ adr 08d0, data 0x00063073 */
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073);
+
+ /* set AVC maximum threshold, adr 08d4, dat ffff0024 */
+ status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval);
+ status = vid_blk_write_word(dev, PATH1_VOL_CTL,
+ (dwval | FLD_PATH1_AVC_THRESHOLD));
+
+ /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */
+ status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval);
+ status = vid_blk_write_word(dev, PATH1_SC_CTL,
+ (dwval | FLD_PATH1_SC_THRESHOLD));
+ break;
+
+ case AUDIO_INPUT_TUNER_TV:
+ default:
+
+ /* Setup SRC sources and clocks */
+ status = vid_blk_write_word(dev, BAND_OUT_SEL,
+ cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) |
+ cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) |
+ cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) |
+ cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) |
+ cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) |
+ cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) |
+ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) |
+ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) |
+ cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) |
+ cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) |
+ cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) |
+ cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) |
+ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01));
+
+ /* Setup the AUD_IO control */
+ status = vid_blk_write_word(dev, AUD_IO_CTRL,
+ cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) |
+ cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) |
+ cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) |
+ cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) |
+ cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03));
+
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870);
+
+ /* setAudioStandard(_audio_standard); */
+
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870);
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ CHIP_CTRL,
+ FLD_SIF_EN,
+ cx231xx_set_field(FLD_SIF_EN, 1));
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case AUDIO_INPUT_TUNER_FM:
+ /* use SIF for FM radio
+ setupFM();
+ setAudioStandard(_audio_standard);
+ */
+ break;
+
+ case AUDIO_INPUT_MUTE:
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012);
+ break;
+ }
+
+ /* Take it out of soft reset */
+ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
+ gen_ctrl &= ~1;
+ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
+
+ return status;
+}
+
+/* Set resolution of the video */
+int cx231xx_resolution_set(struct cx231xx *dev)
+{
+ int width, height;
+ u32 hscale, vscale;
+ int status = 0;
+
+ width = dev->width;
+ height = dev->height;
+
+ get_scale(dev, width, height, &hscale, &vscale);
+
+ /* set horzontal scale */
+ status = vid_blk_write_word(dev, HSCALE_CTRL, hscale);
+
+ /* set vertical scale */
+ status = vid_blk_write_word(dev, VSCALE_CTRL, vscale);
+
+ return status;
+}
+
+/******************************************************************************
+ * C H I P Specific C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_init_ctrl_pin_status(struct cx231xx *dev)
+{
+ u32 value;
+ int status = 0;
+
+ status = vid_blk_read_word(dev, PIN_CTRL, &value);
+ value |= (~dev->board.ctl_pin_status_mask);
+ status = vid_blk_write_word(dev, PIN_CTRL, value);
+
+ return status;
+}
+
+int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
+ u8 analog_or_digital)
+{
+ int status = 0;
+
+ /* first set the direction to output */
+ status = cx231xx_set_gpio_direction(dev,
+ dev->board.
+ agc_analog_digital_select_gpio, 1);
+
+ /* 0 - demod ; 1 - Analog mode */
+ status = cx231xx_set_gpio_value(dev,
+ dev->board.agc_analog_digital_select_gpio,
+ analog_or_digital);
+
+ return status;
+}
+
+int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex)
+{
+ u8 value[4] = { 0, 0, 0, 0 };
+ int status = 0;
+
+ cx231xx_info("Changing the i2c port for tuner to %d\n", I2CIndex);
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ if (status < 0)
+ return status;
+
+ if (I2CIndex == I2C_1) {
+ if (value[0] & I2C_DEMOD_EN) {
+ value[0] &= ~I2C_DEMOD_EN;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ }
+ } else {
+ if (!(value[0] & I2C_DEMOD_EN)) {
+ value[0] |= I2C_DEMOD_EN;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ }
+ }
+
+ return status;
+
+}
+
+/******************************************************************************
+ * D I F - B L O C K C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
+ u32 function_mode, u32 standard)
+{
+ int status = 0;
+
+ if (mode == V4L2_TUNER_RADIO) {
+ /* C2HH */
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ } else if (standard != DIF_USE_BASEBAND) {
+ if (standard & V4L2_STD_MN) {
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
+ function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ /* 0x124, AUD_CHAN1_SRC = 0x3 */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AUD_IO_CTRL, 0, 31, 0x00000003);
+ } else if ((standard == V4L2_STD_PAL_I) |
+ (standard & V4L2_STD_SECAM)) {
+ /* C2HH setup */
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
+ function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ } else {
+ /* default PAL BG */
+ /* C2HH setup */
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
+ function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ }
+ }
+
+ return status;
+}
+
+int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard)
+{
+ int status = 0;
+ u32 dif_misc_ctrl_value = 0;
+ u32 func_mode = 0;
+
+ cx231xx_info("%s: setStandard to %x\n", __func__, standard);
+
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value);
+ if (standard != DIF_USE_BASEBAND)
+ dev->norm = standard;
+
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ func_mode = 0x03;
+ break;
+ default:
+ func_mode = 0x01;
+ }
+
+ status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
+ func_mode, standard);
+
+ if (standard == DIF_USE_BASEBAND) { /* base band */
+ /* There is a different SRC_PHASE_INC value
+ for baseband vs. DIF */
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83);
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+ &