diff options
337 files changed, 32006 insertions, 4298 deletions
diff --git a/linux/Documentation/video4linux/CARDLIST.au0828 b/linux/Documentation/video4linux/CARDLIST.au0828 new file mode 100644 index 000000000..aaae36031 --- /dev/null +++ b/linux/Documentation/video4linux/CARDLIST.au0828 @@ -0,0 +1,4 @@ + 0 -> Unknown board (au0828) + 1 -> Hauppauge HVR950Q (au0828) [2040:7200] + 2 -> Hauppauge HVR850 (au0828) [2040:7240] + 3 -> DViCO FusionHDTV USB (au0828) [0fe9:d620] diff --git a/linux/Documentation/video4linux/CARDLIST.cx23885 b/linux/Documentation/video4linux/CARDLIST.cx23885 index 0924e6e14..191194ea1 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx23885 +++ b/linux/Documentation/video4linux/CARDLIST.cx23885 @@ -5,3 +5,6 @@ 4 -> DViCO FusionHDTV5 Express [18ac:d500] 5 -> Hauppauge WinTV-HVR1500Q [0070:7790,0070:7797] 6 -> Hauppauge WinTV-HVR1500 [0070:7710,0070:7717] + 7 -> Hauppauge WinTV-HVR1200 [0070:71d1,0070:71d3] + 8 -> Hauppauge WinTV-HVR1700 [0070:8101] + 9 -> Hauppauge WinTV-HVR1400 [0070:8010] diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index f40e09296..1d6a245c8 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -14,4 +14,4 @@ 13 -> Terratec Prodigy XS (em2880) [0ccd:0047] 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) 15 -> V-Gear PocketTV (em2800) - 16 -> Hauppauge WinTV HVR 950 (em2880) [2040:6513] + 16 -> Hauppauge WinTV HVR 950 (em2880) [2040:6513,2040:6517,2040:651b,2040:651f] diff --git a/linux/Documentation/video4linux/CARDLIST.saa7134 b/linux/Documentation/video4linux/CARDLIST.saa7134 index fe925b335..67937df1e 100644 --- a/linux/Documentation/video4linux/CARDLIST.saa7134 +++ b/linux/Documentation/video4linux/CARDLIST.saa7134 @@ -25,8 +25,8 @@ 24 -> KNC One TV-Station DVR [1894:a006] 25 -> ASUS TV-FM 7133 [1043:4843] 26 -> Pinnacle PCTV Stereo (saa7134) [11bd:002b] - 27 -> Manli MuchTV M-TV002/Behold TV 403 FM - 28 -> Manli MuchTV M-TV001/Behold TV 401 + 27 -> Manli MuchTV M-TV002 + 28 -> Manli MuchTV M-TV001 29 -> Nagase Sangyo TransGear 3000TV [1461:050c] 30 -> Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) [1019:4cb4] 31 -> Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM) [1019:4cb5] @@ -128,7 +128,7 @@ 127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090] 128 -> Beholder BeholdTV Columbus TVFM [0000:5201] 129 -> Beholder BeholdTV 607 / BeholdTV 609 [5ace:6070,5ace:6071,5ace:6072,5ace:6073,5ace:6090,5ace:6091,5ace:6092,5ace:6093] -130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193] +130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193,5ace:6191] 131 -> Twinhan Hybrid DTV-DVB 3056 PCI [1822:0022] 132 -> Genius TVGO AM11MCE 133 -> NXP Snake DVB-S reference design @@ -140,3 +140,4 @@ 139 -> Compro VideoMate T750 [185b:c900] 140 -> Avermedia DVB-S Pro A700 [1461:a7a1] 141 -> Avermedia DVB-S Hybrid+FM A700 [1461:a7a2] +142 -> Beholder BeholdTV H6 [5ace:6290] diff --git a/linux/Documentation/video4linux/cx18.txt b/linux/Documentation/video4linux/cx18.txt new file mode 100644 index 000000000..6842c2628 --- /dev/null +++ b/linux/Documentation/video4linux/cx18.txt @@ -0,0 +1,36 @@ +Some notes regarding the cx18 driver for the Conexant CX23418 MPEG +encoder chip: + +1) The only hardware currently supported is the Hauppauge HVR-1600 + card and the Compro VideoMate H900 (note that this card only + supports analog input, it has no digital tuner!). + +2) Some people have problems getting the i2c bus to work. Cause unknown. + The symptom is that the eeprom cannot be read and the card is + unusable. + +3) The audio from the analog tuner is mono only. Probably caused by + incorrect audio register information in the datasheet. We are + waiting for updated information from Conexant. + +4) VBI (raw or sliced) has not yet been implemented. + +5) MPEG indexing is not yet implemented. + +6) The driver is still a bit rough around the edges, this should + improve over time. + + +Firmware: + +The firmware needs to be extracted from the Windows Hauppauge HVR-1600 +driver, available here: + +http://hauppauge.lightpath.net/software/install_cd/hauppauge_cd_3.4d1.zip + +Unzip, then copy the following files to the firmware directory +and rename them as follows: + +Drivers/Driver18/hcw18apu.rom -> v4l-cx23418-apu.fw +Drivers/Driver18/hcw18enc.rom -> v4l-cx23418-cpu.fw +Drivers/Driver18/hcw18mlC.rom -> v4l-cx23418-dig.fw diff --git a/linux/Documentation/video4linux/extract_xc3028.pl b/linux/Documentation/video4linux/extract_xc3028.pl index cced8ac5c..2cb816047 100755 --- a/linux/Documentation/video4linux/extract_xc3028.pl +++ b/linux/Documentation/video4linux/extract_xc3028.pl @@ -686,11 +686,11 @@ sub main_firmware($$$$) write_hunk(812664, 192); # - # Firmware 58, type: SCODE FW HAS IF (0x60000000), IF = 4.50 MHz id: NTSC/M Jp (0000000000002000), size: 192 + # Firmware 58, type: SCODE FW MTS LCD NOGD MONO IF HAS IF (0x6002b004), IF = 4.50 MHz id: NTSC PAL/M PAL/N (000000000000b700), size: 192 # - write_le32(0x60000000); # Type - write_le64(0x00000000, 0x00002000); # ID + write_le32(0x6002b004); # Type + write_le64(0x00000000, 0x0000b700); # ID write_le16(4500); # IF write_le32(192); # Size write_hunk(807672, 192); @@ -706,10 +706,10 @@ sub main_firmware($$$$) write_hunk(807864, 192); # - # Firmware 60, type: SCODE FW DTV78 ZARLINK456 HAS IF (0x62000100), IF = 4.76 MHz id: (0000000000000000), size: 192 + # Firmware 60, type: SCODE FW DTV6 QAM DTV7 DTV78 DTV8 ZARLINK456 HAS IF (0x620003e0), IF = 4.76 MHz id: (0000000000000000), size: 192 # - write_le32(0x62000100); # Type + write_le32(0x620003e0); # Type write_le64(0x00000000, 0x00000000); # ID write_le16(4760); # IF write_le32(192); # Size @@ -726,30 +726,30 @@ sub main_firmware($$$$) write_hunk(811512, 192); # - # Firmware 62, type: SCODE FW DTV7 ZARLINK456 HAS IF (0x62000080), IF = 5.26 MHz id: (0000000000000000), size: 192 + # Firmware 62, type: SCODE FW HAS IF (0x60000000), IF = 5.26 MHz id: (0000000000000000), size: 192 # - write_le32(0x62000080); # Type + write_le32(0x60000000); # Type write_le64(0x00000000, 0x00000000); # ID write_le16(5260); # IF write_le32(192); # Size write_hunk(810552, 192); # - # Firmware 63, type: SCODE FW MONO HAS IF (0x60008000), IF = 5.32 MHz id: PAL/BG NICAM/B (0000000800000007), size: 192 + # Firmware 63, type: SCODE FW MONO HAS IF (0x60008000), IF = 5.32 MHz id: PAL/BG A2 NICAM (0000000f00000007), size: 192 # write_le32(0x60008000); # Type - write_le64(0x00000008, 0x00000007); # ID + write_le64(0x0000000f, 0x00000007); # ID write_le16(5320); # IF write_le32(192); # Size write_hunk(810744, 192); # - # Firmware 64, type: SCODE FW DTV8 CHINA HAS IF (0x64000200), IF = 5.40 MHz id: (0000000000000000), size: 192 + # Firmware 64, type: SCODE FW DTV7 DTV78 DTV8 DIBCOM52 CHINA HAS IF (0x65000380), IF = 5.40 MHz id: (0000000000000000), size: 192 # - write_le32(0x64000200); # Type + write_le32(0x65000380); # Type write_le64(0x00000000, 0x00000000); # ID write_le16(5400); # IF write_le32(192); # Size @@ -766,50 +766,50 @@ sub main_firmware($$$$) write_hunk(809592, 192); # - # Firmware 66, type: SCODE FW HAS IF (0x60000000), IF = 5.64 MHz id: PAL/BG A2/B (0000000200000007), size: 192 + # Firmware 66, type: SCODE FW HAS IF (0x60000000), IF = 5.64 MHz id: PAL/BG A2 (0000000300000007), size: 192 # write_le32(0x60000000); # Type - write_le64(0x00000002, 0x00000007); # ID + write_le64(0x00000003, 0x00000007); # ID write_le16(5640); # IF write_le32(192); # Size write_hunk(808440, 192); # - # Firmware 67, type: SCODE FW HAS IF (0x60000000), IF = 5.74 MHz id: PAL/BG NICAM/B (0000000800000007), size: 192 + # Firmware 67, type: SCODE FW HAS IF (0x60000000), IF = 5.74 MHz id: PAL/BG NICAM (0000000c00000007), size: 192 # write_le32(0x60000000); # Type - write_le64(0x00000008, 0x00000007); # ID + write_le64(0x0000000c, 0x00000007); # ID write_le16(5740); # IF write_le32(192); # Size write_hunk(808632, 192); # - # Firmware 68, type: SCODE FW DTV7 DIBCOM52 HAS IF (0x61000080), IF = 5.90 MHz id: (0000000000000000), size: 192 + # Firmware 68, type: SCODE FW HAS IF (0x60000000), IF = 5.90 MHz id: (0000000000000000), size: 192 # - write_le32(0x61000080); # Type + write_le32(0x60000000); # Type write_le64(0x00000000, 0x00000000); # ID write_le16(5900); # IF write_le32(192); # Size write_hunk(810360, 192); # - # Firmware 69, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.00 MHz id: PAL/I (0000000000000010), size: 192 + # Firmware 69, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.00 MHz id: PAL/DK PAL/I SECAM/K3 SECAM/L SECAM/Lc NICAM (0000000c04c000f0), size: 192 # write_le32(0x60008000); # Type - write_le64(0x00000000, 0x00000010); # ID + write_le64(0x0000000c, 0x04c000f0); # ID write_le16(6000); # IF write_le32(192); # Size write_hunk(808824, 192); # - # Firmware 70, type: SCODE FW DTV6 QAM F6MHZ HAS IF (0x68000060), IF = 6.20 MHz id: (0000000000000000), size: 192 + # Firmware 70, type: SCODE FW DTV6 QAM ATSC LG60 F6MHZ HAS IF (0x68050060), IF = 6.20 MHz id: (0000000000000000), size: 192 # - write_le32(0x68000060); # Type + write_le32(0x68050060); # Type write_le64(0x00000000, 0x00000000); # ID write_le16(6200); # IF write_le32(192); # Size @@ -846,11 +846,11 @@ sub main_firmware($$$$) write_hunk(809208, 192); # - # Firmware 74, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.50 MHz id: SECAM/K3 (0000000004000000), size: 192 + # Firmware 74, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.50 MHz id: PAL/DK SECAM/K3 SECAM/L NICAM (0000000c044000e0), size: 192 # write_le32(0x60008000); # Type - write_le64(0x00000000, 0x04000000); # ID + write_le64(0x0000000c, 0x044000e0); # ID write_le16(6500); # IF write_le32(192); # Size write_hunk(811128, 192); diff --git a/linux/arch/arm/mach-pxa/devices.c b/linux/arch/arm/mach-pxa/devices.c index bfccb80ac..d6c05b6ea 100644 --- a/linux/arch/arm/mach-pxa/devices.c +++ b/linux/arch/arm/mach-pxa/devices.c @@ -11,6 +11,8 @@ #include <asm/arch/irda.h> #include <asm/arch/i2c.h> #include <asm/arch/ohci.h> +#include <asm/arch/pxa27x_keypad.h> +#include <asm/arch/camera.h> #include "devices.h" @@ -396,6 +398,31 @@ struct platform_device pxa25x_device_assp = { #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) +static struct resource pxa27x_resource_keypad[] = { + [0] = { + .start = 0x41500000, + .end = 0x4150004c, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_KEYPAD, + .end = IRQ_KEYPAD, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device pxa27x_device_keypad = { + .name = "pxa27x-keypad", + .id = -1, + .resource = pxa27x_resource_keypad, + .num_resources = ARRAY_SIZE(pxa27x_resource_keypad), +}; + +void __init pxa_set_keypad_info(struct pxa27x_keypad_platform_data *info) +{ + pxa_register_device(&pxa27x_device_keypad, info); +} + static u64 pxa27x_ohci_dma_mask = DMA_BIT_MASK(32); static struct resource pxa27x_resource_ohci[] = { @@ -540,6 +567,37 @@ struct platform_device pxa27x_device_ssp3 = { .resource = pxa27x_resource_ssp3, .num_resources = ARRAY_SIZE(pxa27x_resource_ssp3), }; + +static struct resource pxa27x_resource_camera[] = { + [0] = { + .start = 0x50000000, + .end = 0x50000fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_CAMERA, + .end = IRQ_CAMERA, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 pxa27x_dma_mask_camera = DMA_BIT_MASK(32); + +static struct platform_device pxa27x_device_camera = { + .name = "pxa27x-camera", + .id = 0, /* This is used to put cameras on this interface */ + .dev = { + .dma_mask = &pxa27x_dma_mask_camera, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxa27x_resource_camera), + .resource = pxa27x_resource_camera, +}; + +void __init pxa_set_camera_info(struct pxacamera_platform_data *info) +{ + pxa_register_device(&pxa27x_device_camera, info); +} #endif /* CONFIG_PXA27x || CONFIG_PXA3xx */ #ifdef CONFIG_PXA3xx diff --git a/linux/arch/arm/mach-pxa/pcm990-baseboard.c b/linux/arch/arm/mach-pxa/pcm990-baseboard.c index 3dda16a20..49d951db0 100644 --- a/linux/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/linux/arch/arm/mach-pxa/pcm990-baseboard.c @@ -23,8 +23,16 @@ #include <linux/irq.h> #include <linux/platform_device.h> #include <linux/ide.h> +#include <linux/i2c.h> + +#include <media/soc_camera.h> + +#include <asm/gpio.h> +#include <asm/arch/i2c.h> +#include <asm/arch/camera.h> #include <asm/mach/map.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa2xx-gpio.h> #include <asm/arch/mmc.h> #include <asm/arch/ohci.h> #include <asm/arch/pcm990_baseboard.h> @@ -258,6 +266,73 @@ static struct pxaohci_platform_data pcm990_ohci_platform_data = { }; /* + * PXA27x Camera specific stuff + */ +#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) +static int pcm990_pxacamera_init(struct device *dev) +{ + pxa_gpio_mode(GPIO98_CIF_DD_0_MD); + pxa_gpio_mode(GPIO105_CIF_DD_1_MD); + pxa_gpio_mode(GPIO104_CIF_DD_2_MD); + pxa_gpio_mode(GPIO103_CIF_DD_3_MD); + pxa_gpio_mode(GPIO95_CIF_DD_4_MD); + pxa_gpio_mode(GPIO94_CIF_DD_5_MD); + pxa_gpio_mode(GPIO93_CIF_DD_6_MD); + pxa_gpio_mode(GPIO108_CIF_DD_7_MD); + pxa_gpio_mode(GPIO107_CIF_DD_8_MD); + pxa_gpio_mode(GPIO106_CIF_DD_9_MD); + pxa_gpio_mode(GPIO42_CIF_MCLK_MD); + pxa_gpio_mode(GPIO45_CIF_PCLK_MD); + pxa_gpio_mode(GPIO43_CIF_FV_MD); + pxa_gpio_mode(GPIO44_CIF_LV_MD); + + return 0; +} + +/* + * CICR4: PCLK_EN: Pixel clock is supplied by the sensor + * MCLK_EN: Master clock is generated by PXA + * PCP: Data sampled on the falling edge of pixel clock + */ +struct pxacamera_platform_data pcm990_pxacamera_platform_data = { + .init = pcm990_pxacamera_init, + .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | PXA_CAMERA_DATAWIDTH_10 | + PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN/* | PXA_CAMERA_PCP*/, + .mclk_10khz = 1000, +}; + +#include <linux/i2c/pca953x.h> + +static struct pca953x_platform_data pca9536_data = { + .gpio_base = NR_BUILTIN_GPIO + 1, +}; + +static struct soc_camera_link iclink[] = { + { + .bus_id = 0, /* Must match with the camera ID above */ + .gpio = NR_BUILTIN_GPIO + 1, + }, { + .bus_id = 0, /* Must match with the camera ID above */ + } +}; + +/* Board I2C devices. */ +static struct i2c_board_info __initdata pcm990_i2c_devices[] = { + { + /* Must initialize before the camera(s) */ + I2C_BOARD_INFO("pca9536", 0x41), + .platform_data = &pca9536_data, + }, { + I2C_BOARD_INFO("mt9v022", 0x48), + .platform_data = &iclink[0], /* With extender */ + }, { + I2C_BOARD_INFO("mt9m001", 0x5d), + .platform_data = &iclink[0], /* With extender */ + }, +}; +#endif /* CONFIG_VIDEO_PXA27x ||CONFIG_VIDEO_PXA27x_MODULE */ + +/* * AC97 support * Note: The connected AC97 mixer also reports interrupts at PCM990_AC97_IRQ */ @@ -326,5 +401,14 @@ void __init pcm990_baseboard_init(void) /* USB host */ pxa_set_ohci_info(&pcm990_ohci_platform_data); + pxa_set_i2c_info(NULL); + +#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) + pxa_set_camera_info(&pcm990_pxacamera_platform_data); + + i2c_register_board_info(0, pcm990_i2c_devices, + ARRAY_SIZE(pcm990_i2c_devices)); +#endif + printk(KERN_INFO"PCM-990 Evaluation baseboard initialized\n"); } diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index 128bb9cd5..7a7803b5d 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -5,16 +5,20 @@ menu "Multimedia devices" depends on HAS_IOMEM +comment "Multimedia core support" + +# +# V4L core and enabled API's +# + config VIDEO_DEV tristate "Video For Linux" ---help--- - Support for audio/video capture and overlay devices and FM radio - cards. The exact capabilities of each device vary. + V4L core support for video capture and overlay devices, webcams and + AM/FM radio cards. This kernel includes support for the new Video for Linux Two API, - (V4L2) as well as the original system. Drivers and applications - need to be rewritten to use V4L2, but drivers for popular cards - and applications for most video capture functions already exist. + (V4L2). Additional info and docs are available on the web at <http://linuxtv.org> @@ -36,8 +40,11 @@ config VIDEO_ALLOW_V4L1 default VIDEO_DEV && VIDEO_V4L2_COMMON select VIDEO_V4L1_COMPAT ---help--- - Enables a compatibility API used by most V4L2 devices to allow - its usage with legacy applications that supports only V4L1 api. + Enables drivers based on the legacy V4L1 API. + + This api were developed to be used at Kernel 2.2 and 2.4, but + lacks support for several video standards. There are several + drivers at kernel that still depends on it. If you are unsure as to whether this is required, answer Y. @@ -46,9 +53,8 @@ config VIDEO_V4L1_COMPAT depends on VIDEO_DEV default VIDEO_DEV ---help--- - This api were developed to be used at Kernel 2.2 and 2.4, but - lacks support for several video standards. There are several - drivers at kernel that still depends on it. + Enables a compatibility API used by most V4L2 devices to allow + its usage with legacy applications that supports only V4L1 api. Documentation for the original API is included in the file <Documentation/video4linux/API.html>. @@ -58,135 +64,56 @@ config VIDEO_V4L1_COMPAT If you are unsure as to whether this is required, answer Y. -config VIDEO_V4L2 - tristate - depends on VIDEO_DEV && VIDEO_V4L2_COMMON - default VIDEO_DEV && VIDEO_V4L2_COMMON - -config VIDEO_V4L1 - tristate - depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 - default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 - -source "drivers/media/video/Kconfig" - -source "drivers/media/radio/Kconfig" - -source "drivers/media/dvb/Kconfig" - -source "drivers/media/common/Kconfig" +# +# DVB Core +# -config VIDEO_TUNER - tristate - depends on I2C - select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE - select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE - select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE - select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE - select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE - select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE - select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE - -menuconfig VIDEO_TUNER_CUSTOMIZE - bool "Customize analog tuner modules to build" - depends on VIDEO_TUNER +config DVB_CORE + tristate "DVB for Linux" + depends on NET && INET + select CRC32 help - This allows the user to deselect tuner drivers unnecessary - for their hardware from the build. Use this option with care - as deselecting tuner drivers which are in fact necessary will - result in V4L devices which cannot be tuned due to lack of - driver support + DVB core utility functions for device handling, software fallbacks etc. - If unsure say N. - -if VIDEO_TUNER_CUSTOMIZE - -config TUNER_XC2028 - tristate "XCeive xc2028/xc3028 tuners" - depends on I2C && FW_LOADER - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for the xc2028/xc3028 tuners. + Enable this if you own a DVB/ATSC adapter and want to use it or if + you compile Linux for a digital SetTopBox. -config TUNER_MT20XX - tristate "Microtune 2032 / 2050 tuners" - depends on I2C - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for the MT2032 / MT2050 tuner. - -config TUNER_TDA8290 - tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" - depends on I2C - select DVB_TDA827X - select DVB_TDA18271 - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for Philips TDA8290+8275(a) tuner. + Say Y when you have a DVB or an ATSC card and want to use it. -config TUNER_TEA5761 - tristate "TEA 5761 radio tuner (EXPERIMENTAL)" - depends on I2C && EXPERIMENTAL - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for the Philips TEA5761 radio tuner. + API specs and user tools are available from <http://www.linuxtv.org/>. -config TUNER_TEA5767 - tristate "TEA 5767 radio tuner" - depends on I2C - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for the Philips TEA5767 radio tuner. + Please report problems regarding this support to the LinuxDVB + mailing list. -config TUNER_SIMPLE - tristate "Simple tuner support" - depends on I2C - select TUNER_TDA9887 - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for various simple tuners. + If unsure say N. -config TUNER_TDA9887 - tristate "TDA 9885/6/7 analog IF demodulator" - depends on I2C - default m if VIDEO_TUNER_CUSTOMIZE - help - Say Y here to include support for Philips TDA9885/6/7 - analog IF demodulator. +config VIDEO_MEDIA + tristate + default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV) -endif # VIDEO_TUNER_CUSTOMIZE +comment "Multimedia drivers" -config VIDEOBUF_GEN - tristate +source "drivers/media/common/Kconfig" -config VIDEOBUF_DMA_SG - depends on HAS_DMA - select VIDEOBUF_GEN - tristate +# +# Tuner drivers for DVB and V4L +# -config VIDEOBUF_VMALLOC - select VIDEOBUF_GEN - tristate +source "drivers/media/common/tuners/Kconfig" -config VIDEOBUF_DVB - tristate - select VIDEOBUF_GEN - select VIDEOBUF_DMA_SG +# +# Video/Radio/Hybrid adapters +# -config VIDEO_BTCX - tristate +source "drivers/media/video/Kconfig" -config VIDEO_IR_I2C - tristate +source "drivers/media/radio/Kconfig" -config VIDEO_IR - tristate - depends on INPUT - select VIDEO_IR_I2C if I2C +# +# DVB adapters +# -config VIDEO_TVEEPROM - tristate - depends on I2C +source "drivers/media/dvb/Kconfig" config DAB boolean "DAB adapters" diff --git a/linux/drivers/media/Makefile b/linux/drivers/media/Makefile index 7b8bb6949..09a829d8a 100644 --- a/linux/drivers/media/Makefile +++ b/linux/drivers/media/Makefile @@ -2,10 +2,7 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := common/ -obj-y += video/ +obj-y += common/ video/ + obj-$(CONFIG_VIDEO_DEV) += radio/ obj-$(CONFIG_DVB_CORE) += dvb/ -ifeq ($(CONFIG_DVB_CORE),) - obj-$(CONFIG_VIDEO_TUNER) += dvb/frontends/ -endif diff --git a/linux/drivers/media/common/Makefile b/linux/drivers/media/common/Makefile index 8e7448230..351b98b9b 100644 --- a/linux/drivers/media/common/Makefile +++ b/linux/drivers/media/common/Makefile @@ -2,6 +2,7 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o ir-common-objs := ir-functions.o ir-keymaps.o +obj-y += tuners/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o obj-$(CONFIG_VIDEO_IR) += ir-common.o diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 64d3df831..e750bfee1 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2093,6 +2093,76 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { EXPORT_SYMBOL_GPL(ir_codes_behold); +/* Beholder Intl. Ltd. 2008 + * Dmitry Belimov d.belimov@google.com + * Keytable is used by BeholdTV Columbus + * The "ascii-art picture" below (in comments, first row + * is the keycode in hex, and subsequent row(s) shows + * the button labels (several variants when appropriate) + * helps to descide which keycodes to assign to the buttons. + */ +IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE] = { + + /* 0x13 0x11 0x1C 0x12 * + * Mute Source TV/FM Power * + * */ + + [0x13] = KEY_MUTE, + [0x11] = KEY_PROPS, + [0x1C] = KEY_TUNER, /* KEY_TV/KEY_RADIO */ + [0x12] = KEY_POWER, + + /* 0x01 0x02 0x03 0x0D * + * 1 2 3 Stereo * + * * + * 0x04 0x05 0x06 0x19 * + * 4 5 6 Snapshot * + * * + * 0x07 0x08 0x09 0x10 * + * 7 8 9 Zoom * + * */ + [0x01] = KEY_1, + [0x02] = KEY_2, + [0x03] = KEY_3, + [0x0D] = KEY_SETUP, /* Setup key */ + [0x04] = KEY_4, + [0x05] = KEY_5, + [0x06] = KEY_6, + [0x19] = KEY_BOOKMARKS, /* Snapshot key */ + [0x07] = KEY_7, + [0x08] = KEY_8, + [0x09] = KEY_9, + [0x10] = KEY_ZOOM, + + /* 0x0A 0x00 0x0B 0x0C * + * RECALL 0 ChannelUp VolumeUp * + * */ + [0x0A] = KEY_AGAIN, + [0x00] = KEY_0, + [0x0B] = KEY_CHANNELUP, + [0x0C] = KEY_VOLUMEUP, + + /* 0x1B 0x1D 0x15 0x18 * + * Timeshift Record ChannelDown VolumeDown * + * */ + + [0x1B] = KEY_REWIND, + [0x1D] = KEY_RECORD, + [0x15] = KEY_CHANNELDOWN, + [0x18] = KEY_VOLUMEDOWN, + + /* 0x0E 0x1E 0x0F 0x1A * + * Stop Pause Previouse Next * + * */ + + [0x0E] = KEY_STOP, + [0x1E] = KEY_PAUSE, + [0x0F] = KEY_PREVIOUS, + [0x1A] = KEY_NEXT, + +}; +EXPORT_SYMBOL_GPL(ir_codes_behold_columbus); + /* * Remote control for the Genius TVGO A11MCE * Adrian Pardini <pardo.bsso@gmail.com> diff --git a/linux/drivers/media/common/tuners/Kconfig b/linux/drivers/media/common/tuners/Kconfig new file mode 100644 index 000000000..d62065404 --- /dev/null +++ b/linux/drivers/media/common/tuners/Kconfig @@ -0,0 +1,163 @@ +config MEDIA_ATTACH + bool "Load and attach frontend and tuner driver modules as needed" + depends on VIDEO_MEDIA + depends on MODULES + help + Remove the static dependency of DVB card drivers on all + frontend modules for all possible card variants. Instead, + allow the card drivers to only load the frontend modules + they require. + + Also, tuner module will automatically load a tuner driver + when needed, for analog mode. + + This saves several KBytes of memory. + + Note: You will need module-init-tools v3.2 or later for this feature. + + If unsure say Y. + +config MEDIA_TUNER + tristate + default VIDEO_MEDIA && I2C + depends on VIDEO_MEDIA && I2C + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE && HOTPLUG + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE && HOTPLUG + select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMIZE + +menuconfig MEDIA_TUNER_CUSTOMIZE + bool "Customize analog and hybrid tuner modules to build" + depends on MEDIA_TUNER + help + This allows the user to deselect tuner drivers unnecessary + for their hardware from the build. Use this option with care + as deselecting tuner drivers which are in fact necessary will + result in V4L/DVB devices which cannot be tuned due to lack of + driver support + + If unsure say N. + +if MEDIA_TUNER_CUSTOMIZE + +config MEDIA_TUNER_SIMPLE + tristate "Simple tuner support" + depends on VIDEO_MEDIA && I2C + select MEDIA_TUNER_TDA9887 + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for various simple tuners. + +config MEDIA_TUNER_TDA8290 + tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" + depends on VIDEO_MEDIA && I2C + select MEDIA_TUNER_TDA827X + select MEDIA_TUNER_TDA18271 + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for Philips TDA8290+8275(a) tuner. + +config MEDIA_TUNER_TDA827X + tristate "Philips TDA827X silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T silicon tuner module. Say Y when you want to support this tuner. + +config MEDIA_TUNER_TDA18271 + tristate "NXP TDA18271 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A silicon tuner module. Say Y when you want to support this tuner. + +config MEDIA_TUNER_TDA9887 + tristate "TDA 9885/6/7 analog IF demodulator" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for Philips TDA9885/6/7 + analog IF demodulator. + +config MEDIA_TUNER_TEA5761 + tristate "TEA 5761 radio tuner (EXPERIMENTAL)" + depends on VIDEO_MEDIA && I2C + depends on EXPERIMENTAL + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for the Philips TEA5761 radio tuner. + +config MEDIA_TUNER_TEA5767 + tristate "TEA 5767 radio tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for the Philips TEA5767 radio tuner. + +config MEDIA_TUNER_MT20XX + tristate "Microtune 2032 / 2050 tuners" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for the MT2032 / MT2050 tuner. + +config MEDIA_TUNER_MT2060 + tristate "Microtune MT2060 silicon IF tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon IF tuner MT2060 from Microtune. + +config MEDIA_TUNER_MT2266 + tristate "Microtune MT2266 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon baseband tuner MT2266 from Microtune. + +config MEDIA_TUNER_MT2131 + tristate "Microtune MT2131 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon baseband tuner MT2131 from Microtune. + +config MEDIA_TUNER_QT1010 + tristate "Quantek QT1010 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon tuner QT1010 from Quantek. + +config MEDIA_TUNER_XC2028 + tristate "XCeive xc2028/xc3028 tuners" + depends on VIDEO_MEDIA && I2C + depends on HOTPLUG + select FW_LOADER + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to include support for the xc2028/xc3028 tuners. + +config MEDIA_TUNER_XC5000 + tristate "Xceive XC5000 silicon tuner" + depends on VIDEO_MEDIA && I2C + depends on HOTPLUG + select FW_LOADER + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon tuner XC5000 from Xceive. + This device is only used inside a SiP called togther with a + demodulator for now. + +config MEDIA_TUNER_MXL5005S + tristate "MaxLinear MSL5005S silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon tuner MXL5005S from MaxLinear. + +endif # MEDIA_TUNER_CUSTOMIZE diff --git a/linux/drivers/media/common/tuners/Makefile b/linux/drivers/media/common/tuners/Makefile new file mode 100644 index 000000000..55f7e6706 --- /dev/null +++ b/linux/drivers/media/common/tuners/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for common V4L/DVB tuners +# + +tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o + +obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o +obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o +# tuner-types will be merged into tuner-simple, in the future +obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o +obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o +obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o +obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o +obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o +obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o +obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o +obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o +obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o +obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o +obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o +obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o +obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o +obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/linux/drivers/media/dvb/frontends/mt2060.c b/linux/drivers/media/common/tuners/mt2060.c index 355817754..355817754 100644 --- a/linux/drivers/media/dvb/frontends/mt2060.c +++ b/linux/drivers/media/common/tuners/mt2060.c diff --git a/linux/drivers/media/dvb/frontends/mt2060.h b/linux/drivers/media/common/tuners/mt2060.h index acba0058f..cb60caffb 100644 --- a/linux/drivers/media/dvb/frontends/mt2060.h +++ b/linux/drivers/media/common/tuners/mt2060.h @@ -30,7 +30,7 @@ struct mt2060_config { u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ }; -#if defined(CONFIG_DVB_TUNER_MT2060) || (defined(CONFIG_DVB_TUNER_MT2060_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE)) extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1); #else static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) @@ -38,6 +38,6 @@ static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struc printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif // CONFIG_DVB_TUNER_MT2060 +#endif // CONFIG_MEDIA_TUNER_MT2060 #endif diff --git a/linux/drivers/media/dvb/frontends/mt2060_priv.h b/linux/drivers/media/common/tuners/mt2060_priv.h index 5eaccdefd..5eaccdefd 100644 --- a/linux/drivers/media/dvb/frontends/mt2060_priv.h +++ b/linux/drivers/media/common/tuners/mt2060_priv.h diff --git a/linux/drivers/media/video/mt20xx.c b/linux/drivers/media/common/tuners/mt20xx.c index d2c281aeb..d2c281aeb 100644 --- a/linux/drivers/media/video/mt20xx.c +++ b/linux/drivers/media/common/tuners/mt20xx.c diff --git a/linux/drivers/media/video/mt20xx.h b/linux/drivers/media/common/tuners/mt20xx.h index aa848e14c..259553a24 100644 --- a/linux/drivers/media/video/mt20xx.h +++ b/linux/drivers/media/common/tuners/mt20xx.h @@ -20,7 +20,7 @@ #include <linux/i2c.h> #include "dvb_frontend.h" -#if defined(CONFIG_TUNER_MT20XX) || (defined(CONFIG_TUNER_MT20XX_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE)) extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr); diff --git a/linux/drivers/media/dvb/frontends/mt2131.c b/linux/drivers/media/common/tuners/mt2131.c index becc409d9..becc409d9 100644 --- a/linux/drivers/media/dvb/frontends/mt2131.c +++ b/linux/drivers/media/common/tuners/mt2131.c diff --git a/linux/drivers/media/dvb/frontends/mt2131.h b/linux/drivers/media/common/tuners/mt2131.h index 606d8576b..cd8376f6f 100644 --- a/linux/drivers/media/dvb/frontends/mt2131.h +++ b/linux/drivers/media/common/tuners/mt2131.h @@ -30,7 +30,7 @@ struct mt2131_config { u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ }; -#if defined(CONFIG_DVB_TUNER_MT2131) || (defined(CONFIG_DVB_TUNER_MT2131_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE)) extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2131_config *cfg, @@ -44,7 +44,7 @@ static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif /* CONFIG_DVB_TUNER_MT2131 */ +#endif /* CONFIG_MEDIA_TUNER_MT2131 */ #endif /* __MT2131_H__ */ diff --git a/linux/drivers/media/dvb/frontends/mt2131_priv.h b/linux/drivers/media/common/tuners/mt2131_priv.h index e930759c2..e930759c2 100644 --- a/linux/drivers/media/dvb/frontends/mt2131_priv.h +++ b/linux/drivers/media/common/tuners/mt2131_priv.h diff --git a/linux/drivers/media/dvb/frontends/mt2266.c b/linux/drivers/media/common/tuners/mt2266.c index 9927a1a55..9927a1a55 100644 --- a/linux/drivers/media/dvb/frontends/mt2266.c +++ b/linux/drivers/media/common/tuners/mt2266.c diff --git a/linux/drivers/media/dvb/frontends/mt2266.h b/linux/drivers/media/common/tuners/mt2266.h index c5113efe3..4d083882d 100644 --- a/linux/drivers/media/dvb/frontends/mt2266.h +++ b/linux/drivers/media/common/tuners/mt2266.h @@ -24,7 +24,7 @@ struct mt2266_config { u8 i2c_address; }; -#if defined(CONFIG_DVB_TUNER_MT2266) || (defined(CONFIG_DVB_TUNER_MT2266_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE)) extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg); #else static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) @@ -32,6 +32,6 @@ static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struc printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif // CONFIG_DVB_TUNER_MT2266 +#endif // CONFIG_MEDIA_TUNER_MT2266 #endif diff --git a/linux/drivers/media/common/tuners/mxl5005s.c b/linux/drivers/media/common/tuners/mxl5005s.c new file mode 100644 index 000000000..e356db855 --- /dev/null +++ b/linux/drivers/media/common/tuners/mxl5005s.c @@ -0,0 +1,4112 @@ +/* + MaxLinear MXL5005S VSB/QAM/DVBT tuner driver + + Copyright (C) 2008 MaxLinear + Copyright (C) 2006 Steven Toth <stoth@hauppauge.com> + Functions: + mxl5005s_reset() + mxl5005s_writereg() + mxl5005s_writeregs() + mxl5005s_init() + mxl5005s_reconfigure() + mxl5005s_AssignTunerMode() + mxl5005s_set_params() + mxl5005s_get_frequency() + mxl5005s_get_bandwidth() + mxl5005s_release() + mxl5005s_attach() + + Copyright (C) 2008 Realtek + Copyright (C) 2008 Jan Hoogenraad + Functions: + mxl5005s_SetRfFreqHz() + + 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. + +*/ + +/* + History of this driver (Steven Toth): + I was given a public release of a linux driver that included + support for the MaxLinear MXL5005S silicon tuner. Analysis of + the tuner driver showed clearly three things. + + 1. The tuner driver didn't support the LinuxTV tuner API + so the code Realtek added had to be removed. + + 2. A significant amount of the driver is reference driver code + from MaxLinear, I felt it was important to identify and + preserve this. + + 3. New code has to be added to interface correctly with the + LinuxTV API, as a regular kernel module. + + Other than the reference driver enum's, I've clearly marked + sections of the code and retained the copyright of the + respective owners. +*/ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "mxl5005s.h" + +static int debug; + +#define dprintk(level, arg...) do { \ + if (level <= debug) \ + printk(arg); \ + } while (0) + +#define TUNER_REGS_NUM 104 +#define INITCTRL_NUM 40 + +#ifdef _MXL_PRODUCTION +#define CHCTRL_NUM 39 +#else +#define CHCTRL_NUM 36 +#endif + +#define MXLCTRL_NUM 189 +#define MASTER_CONTROL_ADDR 9 + +/* Enumeration of Master Control Register State */ +enum master_control_state { + MC_LOAD_START = 1, + MC_POWER_DOWN, + MC_SYNTH_RESET, + MC_SEQ_OFF +}; + +/* Enumeration of MXL5005 Tuner Modulation Type */ +enum { + MXL_DEFAULT_MODULATION = 0, + MXL_DVBT, + MXL_ATSC, + MXL_QAM, + MXL_ANALOG_CABLE, + MXL_ANALOG_OTA +}; + +/* MXL5005 Tuner Register Struct */ +struct TunerReg { + u16 Reg_Num; /* Tuner Register Address */ + u16 Reg_Val; /* Current sw programmed value waiting to be writen */ +}; + +enum { + /* Initialization Control Names */ + DN_IQTN_AMP_CUT = 1, /* 1 */ + BB_MODE, /* 2 */ + BB_BUF, /* 3 */ + BB_BUF_OA, /* 4 */ + BB_ALPF_BANDSELECT, /* 5 */ + BB_IQSWAP, /* 6 */ + BB_DLPF_BANDSEL, /* 7 */ + RFSYN_CHP_GAIN, /* 8 */ + RFSYN_EN_CHP_HIGAIN, /* 9 */ + AGC_IF, /* 10 */ + AGC_RF, /* 11 */ + IF_DIVVAL, /* 12 */ + IF_VCO_BIAS, /* 13 */ + CHCAL_INT_MOD_IF, /* 14 */ + CHCAL_FRAC_MOD_IF, /* 15 */ + DRV_RES_SEL, /* 16 */ + I_DRIVER, /* 17 */ + EN_AAF, /* 18 */ + EN_3P, /* 19 */ + EN_AUX_3P, /* 20 */ + SEL_AAF_BAND, /* 21 */ + SEQ_ENCLK16_CLK_OUT, /* 22 */ + SEQ_SEL4_16B, /* 23 */ + XTAL_CAPSELECT, /* 24 */ + IF_SEL_DBL, /* 25 */ + RFSYN_R_DIV, /* 26 */ + SEQ_EXTSYNTHCALIF, /* 27 */ + SEQ_EXTDCCAL, /* 28 */ + AGC_EN_RSSI, /* 29 */ + RFA_ENCLKRFAGC, /* 30 */ + RFA_RSSI_REFH, /* 31 */ + RFA_RSSI_REF, /* 32 */ + RFA_RSSI_REFL, /* 33 */ + RFA_FLR, /* 34 */ + RFA_CEIL, /* 35 */ + SEQ_EXTIQFSMPULSE, /* 36 */ + OVERRIDE_1, /* 37 */ + BB_INITSTATE_DLPF_TUNE, /* 38 */ + TG_R_DIV, /* 39 */ + EN_CHP_LIN_B, /* 40 */ + + /* Channel Change Control Names */ + DN_POLY = 51, /* 51 */ + DN_RFGAIN, /* 52 */ + DN_CAP_RFLPF, /* 53 */ + DN_EN_VHFUHFBAR, /* 54 */ + DN_GAIN_ADJUST, /* 55 */ + DN_IQTNBUF_AMP, /* 56 */ + DN_IQTNGNBFBIAS_BST, /* 57 */ + RFSYN_EN_OUTMUX, /* 58 */ + RFSYN_SEL_VCO_OUT, /* 59 */ + RFSYN_SEL_VCO_HI, /* 60 */ + RFSYN_SEL_DIVM, /* 61 */ + RFSYN_RF_DIV_BIAS, /* 62 */ + DN_SEL_FREQ, /* 63 */ + RFSYN_VCO_BIAS, /* 64 */ + CHCAL_INT_MOD_RF, /* 65 */ + CHCAL_FRAC_MOD_RF, /* 66 */ + RFSYN_LPF_R, /* 67 */ + CHCAL_EN_INT_RF, /* 68 */ + TG_LO_DIVVAL, /* 69 */ + TG_LO_SELVAL, /* 70 */ + TG_DIV_VAL, /* 71 */ + TG_VCO_BIAS, /* 72 */ + SEQ_EXTPOWERUP, /* 73 */ + OVERRIDE_2, /* 74 */ + OVERRIDE_3, /* 75 */ + OVERRIDE_4, /* 76 */ + SEQ_FSM_PULSE, /* 77 */ + GPIO_4B, /* 78 */ + GPIO_3B, /* 79 */ + GPIO_4, /* 80 */ + GPIO_3, /* 81 */ + GPIO_1B, /* 82 */ + DAC_A_ENABLE, /* 83 */ + DAC_B_ENABLE, /* 84 */ + DAC_DIN_A, /* 85 */ + DAC_DIN_B, /* 86 */ +#ifdef _MXL_PRODUCTION + RFSYN_EN_DIV, /* 87 */ + RFSYN_DIVM, /* 88 */ + DN_BYPASS_AGC_I2C /* 89 */ +#endif +}; + +/* + * The following context is source code provided by MaxLinear. + * MaxLinear source code - Common_MXL.h (?) + */ + +/* Constants */ +#define MXL5005S_REG_WRITING_TABLE_LEN_MAX 104 +#define MXL5005S_LATCH_BYTE 0xfe + +/* Register address, MSB, and LSB */ +#define MXL5005S_BB_IQSWAP_ADDR 59 +#define MXL5005S_BB_IQSWAP_MSB 0 +#define MXL5005S_BB_IQSWAP_LSB 0 + +#define MXL5005S_BB_DLPF_BANDSEL_ADDR 53 +#define MXL5005S_BB_DLPF_BANDSEL_MSB 4 +#define MXL5005S_BB_DLPF_BANDSEL_LSB 3 + +/* Standard modes */ +enum { + MXL5005S_STANDARD_DVBT, + MXL5005S_STANDARD_ATSC, +}; +#define MXL5005S_STANDARD_MODE_NUM 2 + +/* Bandwidth modes */ +enum { + MXL5005S_BANDWIDTH_6MHZ = 6000000, + MXL5005S_BANDWIDTH_7MHZ = 7000000, + MXL5005S_BANDWIDTH_8MHZ = 8000000, +}; +#define MXL5005S_BANDWIDTH_MODE_NUM 3 + +/* MXL5005 Tuner Control Struct */ +struct TunerControl { + u16 Ctrl_Num; /* Control Number */ + u16 size; /* Number of bits to represent Value */ + u16 addr[25]; /* Array of Tuner Register Address for each bit pos */ + u16 bit[25]; /* Array of bit pos in Reg Addr for each bit pos */ + u16 val[25]; /* Binary representation of Value */ +}; + +/* MXL5005 Tuner Struct */ +struct mxl5005s_state { + u8 Mode; /* 0: Analog Mode ; 1: Digital Mode */ + u8 IF_Mode; /* for Analog Mode, 0: zero IF; 1: low IF */ + u32 Chan_Bandwidth; /* filter channel bandwidth (6, 7, 8) */ + u32 IF_OUT; /* Desired IF Out Frequency */ + u16 IF_OUT_LOAD; /* IF Out Load Resistor (200/300 Ohms) */ + u32 RF_IN; /* RF Input Frequency */ + u32 Fxtal; /* XTAL Frequency */ + u8 AGC_Mode; /* AGC Mode 0: Dual AGC; 1: Single AGC */ + u16 TOP; /* Value: take over point */ + u8 CLOCK_OUT; /* 0: turn off clk out; 1: turn on clock out */ + u8 DIV_OUT; /* 4MHz or 16MHz */ + u8 CAPSELECT; /* 0: disable On-Chip pulling cap; 1: enable */ + u8 EN_RSSI; /* 0: disable RSSI; 1: enable RSSI */ + + /* Modulation Type; */ + /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ + u8 Mod_Type; + + /* Tracking Filter Type */ + /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ + u8 TF_Type; + + /* Calculated Settings */ + u32 RF_LO; /* Synth RF LO Frequency */ + u32 IF_LO; /* Synth IF LO Frequency */ + u32 TG_LO; /* Synth TG_LO Frequency */ + + /* Pointers to ControlName Arrays */ + u16 Init_Ctrl_Num; /* Number of INIT Control Names */ + struct TunerControl + Init_Ctrl[INITCTRL_NUM]; /* INIT Control Names Array Pointer */ + + u16 CH_Ctrl_Num; /* Number of CH Control Names */ + struct TunerControl + CH_Ctrl[CHCTRL_NUM]; /* CH Control Name Array Pointer */ + + u16 MXL_Ctrl_Num; /* Number of MXL Control Names */ + struct TunerControl + MXL_Ctrl[MXLCTRL_NUM]; /* MXL Control Name Array Pointer */ + + /* Pointer to Tuner Register Array */ + u16 TunerRegs_Num; /* Number of Tuner Registers */ + struct TunerReg + TunerRegs[TUNER_REGS_NUM]; /* Tuner Register Array Pointer */ + + /* Linux driver framework specific */ + struct mxl5005s_config *config; + struct dvb_frontend *frontend; + struct i2c_adapter *i2c; + + /* Cache values */ + u32 current_mode; + +}; + +static u16 MXL_GetMasterControl(u8 *MasterReg, int state); +static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value); +static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value); +static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, + u8 bitVal); +static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count); +static u32 MXL_Ceiling(u32 value, u32 resolution); +static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal); +static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, + u32 value, u16 controlGroup); +static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val); +static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count); +static u32 MXL_GetXtalInt(u32 Xtal_Freq); +static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq); +static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe); +static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe); +static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count); +static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, + u8 *datatable, u8 len); +static u16 MXL_IFSynthInit(struct dvb_frontend *fe); +static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth); +static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth); + +/* ---------------------------------------------------------------- + * Begin: Custom code salvaged from the Realtek driver. + * Copyright (C) 2008 Realtek + * Copyright (C) 2008 Jan Hoogenraad + * This code is placed under the terms of the GNU General Public License + * + * Released by Realtek under GPLv2. + * Thanks to Realtek for a lot of support we received ! + * + * Revision: 080314 - original version + */ + +static int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz) +{ + struct mxl5005s_state *state = fe->tuner_priv; + unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + int TableLen; + + u32 IfDivval = 0; + unsigned char MasterControlByte; + + dprintk(1, "%s() freq=%ld\n", __func__, RfFreqHz); + + /* Set MxL5005S tuner RF frequency according to example code. */ + + /* Tuner RF frequency setting stage 0 */ + MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); + AddrTable[0] = MASTER_CONTROL_ADDR; + ByteTable[0] |= state->config->AgcMasterByte; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); + + /* Tuner RF frequency setting stage 1 */ + MXL_TuneRF(fe, RfFreqHz); + + MXL_ControlRead(fe, IF_DIVVAL, &IfDivval); + + MXL_ControlWrite(fe, SEQ_FSM_PULSE, 0); + MXL_ControlWrite(fe, SEQ_EXTPOWERUP, 1); + MXL_ControlWrite(fe, IF_DIVVAL, 8); + MXL_GetCHRegister(fe, AddrTable, ByteTable, &TableLen); + + MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); + AddrTable[TableLen] = MASTER_CONTROL_ADDR ; + ByteTable[TableLen] = MasterControlByte | + state->config->AgcMasterByte; + TableLen += 1; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + + /* Wait 30 ms. */ + msleep(150); + + /* Tuner RF frequency setting stage 2 */ + MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1); + MXL_ControlWrite(fe, IF_DIVVAL, IfDivval); + MXL_GetCHRegister_ZeroIF(fe, AddrTable, ByteTable, &TableLen); + + MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); + AddrTable[TableLen] = MASTER_CONTROL_ADDR ; + ByteTable[TableLen] = MasterControlByte | + state->config->AgcMasterByte ; + TableLen += 1; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + + msleep(100); + + return 0; +} +/* End: Custom code taken from the Realtek driver */ + +/* ---------------------------------------------------------------- + * Begin: Reference driver code found in the Realtek driver. + * Copyright (C) 2008 MaxLinear + */ +static u16 MXL5005_RegisterInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + state->TunerRegs_Num = TUNER_REGS_NUM ; + + state->TunerRegs[0].Reg_Num = 9 ; + state->TunerRegs[0].Reg_Val = 0x40 ; + + state->TunerRegs[1].Reg_Num = 11 ; + state->TunerRegs[1].Reg_Val = 0x19 ; + + state->TunerRegs[2].Reg_Num = 12 ; + state->TunerRegs[2].Reg_Val = 0x60 ; + + state->TunerRegs[3].Reg_Num = 13 ; + state->TunerRegs[3].Reg_Val = 0x00 ; + + state->TunerRegs[4].Reg_Num = 14 ; + state->TunerRegs[4].Reg_Val = 0x00 ; + + state->TunerRegs[5].Reg_Num = 15 ; + state->TunerRegs[5].Reg_Val = 0xC0 ; + + state->TunerRegs[6].Reg_Num = 16 ; + state->TunerRegs[6].Reg_Val = 0x00 ; + + state->TunerRegs[7].Reg_Num = 17 ; + state->TunerRegs[7].Reg_Val = 0x00 ; + + state->TunerRegs[8].Reg_Num = 18 ; + state->TunerRegs[8].Reg_Val = 0x00 ; + + state->TunerRegs[9].Reg_Num = 19 ; + state->TunerRegs[9].Reg_Val = 0x34 ; + + state->TunerRegs[10].Reg_Num = 21 ; + state->TunerRegs[10].Reg_Val = 0x00 ; + + state->TunerRegs[11].Reg_Num = 22 ; + state->TunerRegs[11].Reg_Val = 0x6B ; + + state->TunerRegs[12].Reg_Num = 23 ; + state->TunerRegs[12].Reg_Val = 0x35 ; + + state->TunerRegs[13].Reg_Num = 24 ; + state->TunerRegs[13].Reg_Val = 0x70 ; + + state->TunerRegs[14].Reg_Num = 25 ; + state->TunerRegs[14].Reg_Val = 0x3E ; + + state->TunerRegs[15].Reg_Num = 26 ; + state->TunerRegs[15].Reg_Val = 0x82 ; + + state->TunerRegs[16].Reg_Num = 31 ; + state->TunerRegs[16].Reg_Val = 0x00 ; + + state->TunerRegs[17].Reg_Num = 32 ; + state->TunerRegs[17].Reg_Val = 0x40 ; + + state->TunerRegs[18].Reg_Num = 33 ; + state->TunerRegs[18].Reg_Val = 0x53 ; + + state->TunerRegs[19].Reg_Num = 34 ; + state->TunerRegs[19].Reg_Val = 0x81 ; + + state->TunerRegs[20].Reg_Num = 35 ; + state->TunerRegs[20].Reg_Val = 0xC9 ; + + state->TunerRegs[21].Reg_Num = 36 ; + state->TunerRegs[21].Reg_Val = 0x01 ; + + state->TunerRegs[22].Reg_Num = 37 ; + state->TunerRegs[22].Reg_Val = 0x00 ; + + state->TunerRegs[23].Reg_Num = 41 ; + state->TunerRegs[23].Reg_Val = 0x00 ; + + state->TunerRegs[24].Reg_Num = 42 ; + state->TunerRegs[24].Reg_Val = 0xF8 ; + + state->TunerRegs[25].Reg_Num = 43 ; + state->TunerRegs[25].Reg_Val = 0x43 ; + + state->TunerRegs[26].Reg_Num = 44 ; + state->TunerRegs[26].Reg_Val = 0x20 ; + + state->TunerRegs[27].Reg_Num = 45 ; + state->TunerRegs[27].Reg_Val = 0x80 ; + + state->TunerRegs[28].Reg_Num = 46 ; + state->TunerRegs[28].Reg_Val = 0x88 ; + + state->TunerRegs[29].Reg_Num = 47 ; + state->TunerRegs[29].Reg_Val = 0x86 ; + + state->TunerRegs[30].Reg_Num = 48 ; + state->TunerRegs[30].Reg_Val = 0x00 ; + + state->TunerRegs[31].Reg_Num = 49 ; + state->TunerRegs[31].Reg_Val = 0x00 ; + + state->TunerRegs[32].Reg_Num = 53 ; + state->TunerRegs[32].Reg_Val = 0x94 ; + + state->TunerRegs[33].Reg_Num = 54 ; + state->TunerRegs[33].Reg_Val = 0xFA ; + + state->TunerRegs[34].Reg_Num = 55 ; + state->TunerRegs[34].Reg_Val = 0x92 ; + + state->TunerRegs[35].Reg_Num = 56 ; + state->TunerRegs[35].Reg_Val = 0x80 ; + + state->TunerRegs[36].Reg_Num = 57 ; + state->TunerRegs[36].Reg_Val = 0x41 ; + + state->TunerRegs[37].Reg_Num = 58 ; + state->TunerRegs[37].Reg_Val = 0xDB ; + + state->TunerRegs[38].Reg_Num = 59 ; + state->TunerRegs[38].Reg_Val = 0x00 ; + + state->TunerRegs[39].Reg_Num = 60 ; + state->TunerRegs[39].Reg_Val = 0x00 ; + + state->TunerRegs[40].Reg_Num = 61 ; + state->TunerRegs[40].Reg_Val = 0x00 ; + + state->TunerRegs[41].Reg_Num = 62 ; + state->TunerRegs[41].Reg_Val = 0x00 ; + + state->TunerRegs[42].Reg_Num = 65 ; + state->TunerRegs[42].Reg_Val = 0xF8 ; + + state->TunerRegs[43].Reg_Num = 66 ; + state->TunerRegs[43].Reg_Val = 0xE4 ; + + state->TunerRegs[44].Reg_Num = 67 ; + state->TunerRegs[44].Reg_Val = 0x90 ; + + state->TunerRegs[45].Reg_Num = 68 ; + state->TunerRegs[45].Reg_Val = 0xC0 ; + + state->TunerRegs[46].Reg_Num = 69 ; + state->TunerRegs[46].Reg_Val = 0x01 ; + + state->TunerRegs[47].Reg_Num = 70 ; + state->TunerRegs[47].Reg_Val = 0x50 ; + + state->TunerRegs[48].Reg_Num = 71 ; + state->TunerRegs[48].Reg_Val = 0x06 ; + + state->TunerRegs[49].Reg_Num = 72 ; + state->TunerRegs[49].Reg_Val = 0x00 ; + + state->TunerRegs[50].Reg_Num = 73 ; + state->TunerRegs[50].Reg_Val = 0x20 ; + + state->TunerRegs[51].Reg_Num = 76 ; + state->TunerRegs[51].Reg_Val = 0xBB ; + + state->TunerRegs[52].Reg_Num = 77 ; + state->TunerRegs[52].Reg_Val = 0x13 ; + + state->TunerRegs[53].Reg_Num = 81 ; + state->TunerRegs[53].Reg_Val = 0x04 ; + + state->TunerRegs[54].Reg_Num = 82 ; + state->TunerRegs[54].Reg_Val = 0x75 ; + + state->TunerRegs[55].Reg_Num = 83 ; + state->TunerRegs[55].Reg_Val = 0x00 ; + + state->TunerRegs[56].Reg_Num = 84 ; + state->TunerRegs[56].Reg_Val = 0x00 ; + + state->TunerRegs[57].Reg_Num = 85 ; + state->TunerRegs[57].Reg_Val = 0x00 ; + + state->TunerRegs[58].Reg_Num = 91 ; + state->TunerRegs[58].Reg_Val = 0x70 ; + + state->TunerRegs[59].Reg_Num = 92 ; + state->TunerRegs[59].Reg_Val = 0x00 ; + + state->TunerRegs[60].Reg_Num = 93 ; + state->TunerRegs[60].Reg_Val = 0x00 ; + + state->TunerRegs[61].Reg_Num = 94 ; + state->TunerRegs[61].Reg_Val = 0x00 ; + + state->TunerRegs[62].Reg_Num = 95 ; + state->TunerRegs[62].Reg_Val = 0x0C ; + + state->TunerRegs[63].Reg_Num = 96 ; + state->TunerRegs[63].Reg_Val = 0x00 ; + + state->TunerRegs[64].Reg_Num = 97 ; + state->TunerRegs[64].Reg_Val = 0x00 ; + + state->TunerRegs[65].Reg_Num = 98 ; + state->TunerRegs[65].Reg_Val = 0xE2 ; + + state->TunerRegs[66].Reg_Num = 99 ; + state->TunerRegs[66].Reg_Val = 0x00 ; + + state->TunerRegs[67].Reg_Num = 100 ; + state->TunerRegs[67].Reg_Val = 0x00 ; + + state->TunerRegs[68].Reg_Num = 101 ; + state->TunerRegs[68].Reg_Val = 0x12 ; + + state->TunerRegs[69].Reg_Num = 102 ; + state->TunerRegs[69].Reg_Val = 0x80 ; + + state->TunerRegs[70].Reg_Num = 103 ; + state->TunerRegs[70].Reg_Val = 0x32 ; + + state->TunerRegs[71].Reg_Num = 104 ; + state->TunerRegs[71].Reg_Val = 0xB4 ; + + state->TunerRegs[72].Reg_Num = 105 ; + state->TunerRegs[72].Reg_Val = 0x60 ; + + state->TunerRegs[73].Reg_Num = 106 ; + state->TunerRegs[73].Reg_Val = 0x83 ; + + state->TunerRegs[74].Reg_Num = 107 ; + state->TunerRegs[74].Reg_Val = 0x84 ; + + state->TunerRegs[75].Reg_Num = 108 ; + state->TunerRegs[75].Reg_Val = 0x9C ; + + state->TunerRegs[76].Reg_Num = 109 ; + state->TunerRegs[76].Reg_Val = 0x02 ; + + state->TunerRegs[77].Reg_Num = 110 ; + state->TunerRegs[77].Reg_Val = 0x81 ; + + state->TunerRegs[78].Reg_Num = 111 ; + state->TunerRegs[78].Reg_Val = 0xC0 ; + + state->TunerRegs[79].Reg_Num = 112 ; + state->TunerRegs[79].Reg_Val = 0x10 ; + + state->TunerRegs[80].Reg_Num = 131 ; + state->TunerRegs[80].Reg_Val = 0x8A ; + + state->TunerRegs[81].Reg_Num = 132 ; + state->TunerRegs[81].Reg_Val = 0x10 ; + + state->TunerRegs[82].Reg_Num = 133 ; + state->TunerRegs[82].Reg_Val = 0x24 ; + + state->TunerRegs[83].Reg_Num = 134 ; + state->TunerRegs[83].Reg_Val = 0x00 ; + + state->TunerRegs[84].Reg_Num = 135 ; + state->TunerRegs[84].Reg_Val = 0x00 ; + + state->TunerRegs[85].Reg_Num = 136 ; + state->TunerRegs[85].Reg_Val = 0x7E ; + + state->TunerRegs[86].Reg_Num = 137 ; + state->TunerRegs[86].Reg_Val = 0x40 ; + + state->TunerRegs[87].Reg_Num = 138 ; + state->TunerRegs[87].Reg_Val = 0x38 ; + + state->TunerRegs[88].Reg_Num = 146 ; + state->TunerRegs[88].Reg_Val = 0xF6 ; + + state->TunerRegs[89].Reg_Num = 147 ; + state->TunerRegs[89].Reg_Val = 0x1A ; + + state->TunerRegs[90].Reg_Num = 148 ; + state->TunerRegs[90].Reg_Val = 0x62 ; + + state->TunerRegs[91].Reg_Num = 149 ; + state->TunerRegs[91].Reg_Val = 0x33 ; + + state->TunerRegs[92].Reg_Num = 150 ; + state->TunerRegs[92].Reg_Val = 0x80 ; + + state->TunerRegs[93].Reg_Num = 156 ; + state->TunerRegs[93].Reg_Val = 0x56 ; + + state->TunerRegs[94].Reg_Num = 157 ; + state->TunerRegs[94].Reg_Val = 0x17 ; + + state->TunerRegs[95].Reg_Num = 158 ; + state->TunerRegs[95].Reg_Val = 0xA9 ; + + state->TunerRegs[96].Reg_Num = 159 ; + state->TunerRegs[96].Reg_Val = 0x00 ; + + state->TunerRegs[97].Reg_Num = 160 ; + state->TunerRegs[97].Reg_Val = 0x00 ; + + state->TunerRegs[98].Reg_Num = 161 ; + state->TunerRegs[98].Reg_Val = 0x00 ; + + state->TunerRegs[99].Reg_Num = 162 ; + state->TunerRegs[99].Reg_Val = 0x40 ; + + state->TunerRegs[100].Reg_Num = 166 ; + state->TunerRegs[100].Reg_Val = 0xAE ; + + state->TunerRegs[101].Reg_Num = 167 ; + state->TunerRegs[101].Reg_Val = 0x1B ; + + state->TunerRegs[102].Reg_Num = 168 ; + state->TunerRegs[102].Reg_Val = 0xF2 ; + + state->TunerRegs[103].Reg_Num = 195 ; + state->TunerRegs[103].Reg_Val = 0x00 ; + + return 0 ; +} + +static u16 MXL5005_ControlInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + state->Init_Ctrl_Num = INITCTRL_NUM; + + state->Init_Ctrl[0].Ctrl_Num = DN_IQTN_AMP_CUT ; + state->Init_Ctrl[0].size = 1 ; + state->Init_Ctrl[0].addr[0] = 73; + state->Init_Ctrl[0].bit[0] = 7; + state->Init_Ctrl[0].val[0] = 0; + + state->Init_Ctrl[1].Ctrl_Num = BB_MODE ; + state->Init_Ctrl[1].size = 1 ; + state->Init_Ctrl[1].addr[0] = 53; + state->Init_Ctrl[1].bit[0] = 2; + state->Init_Ctrl[1].val[0] = 1; + + state->Init_Ctrl[2].Ctrl_Num = BB_BUF ; + state->Init_Ctrl[2].size = 2 ; + state->Init_Ctrl[2].addr[0] = 53; + state->Init_Ctrl[2].bit[0] = 1; + state->Init_Ctrl[2].val[0] = 0; + state->Init_Ctrl[2].addr[1] = 57; + state->Init_Ctrl[2].bit[1] = 0; + state->Init_Ctrl[2].val[1] = 1; + + state->Init_Ctrl[3].Ctrl_Num = BB_BUF_OA ; + state->Init_Ctrl[3].size = 1 ; + state->Init_Ctrl[3].addr[0] = 53; + state->Init_Ctrl[3].bit[0] = 0; + state->Init_Ctrl[3].val[0] = 0; + + state->Init_Ctrl[4].Ctrl_Num = BB_ALPF_BANDSELECT ; + state->Init_Ctrl[4].size = 3 ; + state->Init_Ctrl[4].addr[0] = 53; + state->Init_Ctrl[4].bit[0] = 5; + state->Init_Ctrl[4].val[0] = 0; + state->Init_Ctrl[4].addr[1] = 53; + state->Init_Ctrl[4].bit[1] = 6; + state->Init_Ctrl[4].val[1] = 0; + state->Init_Ctrl[4].addr[2] = 53; + state->Init_Ctrl[4].bit[2] = 7; + state->Init_Ctrl[4].val[2] = 1; + + state->Init_Ctrl[5].Ctrl_Num = BB_IQSWAP ; + state->Init_Ctrl[5].size = 1 ; + state->Init_Ctrl[5].addr[0] = 59; + state->Init_Ctrl[5].bit[0] = 0; + state->Init_Ctrl[5].val[0] = 0; + + state->Init_Ctrl[6].Ctrl_Num = BB_DLPF_BANDSEL ; + state->Init_Ctrl[6].size = 2 ; + state->Init_Ctrl[6].addr[0] = 53; + state->Init_Ctrl[6].bit[0] = 3; + state->Init_Ctrl[6].val[0] = 0; + state->Init_Ctrl[6].addr[1] = 53; + state->Init_Ctrl[6].bit[1] = 4; + state->Init_Ctrl[6].val[1] = 1; + + state->Init_Ctrl[7].Ctrl_Num = RFSYN_CHP_GAIN ; + state->Init_Ctrl[7].size = 4 ; + state->Init_Ctrl[7].addr[0] = 22; + state->Init_Ctrl[7].bit[0] = 4; + state->Init_Ctrl[7].val[0] = 0; + state->Init_Ctrl[7].addr[1] = 22; + state->Init_Ctrl[7].bit[1] = 5; + state->Init_Ctrl[7].val[1] = 1; + state->Init_Ctrl[7].addr[2] = 22; + state->Init_Ctrl[7].bit[2] = 6; + state->Init_Ctrl[7].val[2] = 1; + state->Init_Ctrl[7].addr[3] = 22; + state->Init_Ctrl[7].bit[3] = 7; + state->Init_Ctrl[7].val[3] = 0; + + state->Init_Ctrl[8].Ctrl_Num = RFSYN_EN_CHP_HIGAIN ; + state->Init_Ctrl[8].size = 1 ; + state->Init_Ctrl[8].addr[0] = 22; + state->Init_Ctrl[8].bit[0] = 2; + state->Init_Ctrl[8].val[0] = 0; + + state->Init_Ctrl[9].Ctrl_Num = AGC_IF ; + state->Init_Ctrl[9].size = 4 ; + state->Init_Ctrl[9].addr[0] = 76; + state->Init_Ctrl[9].bit[0] = 0; + state->Init_Ctrl[9].val[0] = 1; + state->Init_Ctrl[9].addr[1] = 76; + state->Init_Ctrl[9].bit[1] = 1; + state->Init_Ctrl[9].val[1] = 1; + state->Init_Ctrl[9].addr[2] = 76; + state->Init_Ctrl[9].bit[2] = 2; + state->Init_Ctrl[9].val[2] = 0; + state->Init_Ctrl[9].addr[3] = 76; + state->Init_Ctrl[9].bit[3] = 3; + state->Init_Ctrl[9].val[3] = 1; + + state->Init_Ctrl[10].Ctrl_Num = AGC_RF ; + state->Init_Ctrl[10].size = 4 ; + state->Init_Ctrl[10].addr[0] = 76; + state->Init_Ctrl[10].bit[0] = 4; + state->Init_Ctrl[10].val[0] = 1; + state->Init_Ctrl[10].addr[1] = 76; + state->Init_Ctrl[10].bit[1] = 5; + state->Init_Ctrl[10].val[1] = 1; + state->Init_Ctrl[10].addr[2] = 76; + state->Init_Ctrl[10].bit[2] = 6; + state->Init_Ctrl[10].val[2] = 0; + state->Init_Ctrl[10].addr[3] = 76; + state->Init_Ctrl[10].bit[3] = 7; + state->Init_Ctrl[10].val[3] = 1; + + state->Init_Ctrl[11].Ctrl_Num = IF_DIVVAL ; + state->Init_Ctrl[11].size = 5 ; + state->Init_Ctrl[11].addr[0] = 43; + state->Init_Ctrl[11].bit[0] = 3; + state->Init_Ctrl[11].val[0] = 0; + state->Init_Ctrl[11].addr[1] = 43; + state->Init_Ctrl[11].bit[1] = 4; + state->Init_Ctrl[11].val[1] = 0; + state->Init_Ctrl[11].addr[2] = 43; + state->Init_Ctrl[11].bit[2] = 5; + state->Init_Ctrl[11].val[2] = 0; + state->Init_Ctrl[11].addr[3] = 43; + state->Init_Ctrl[11].bit[3] = 6; + state->Init_Ctrl[11].val[3] = 1; + state->Init_Ctrl[11].addr[4] = 43; + state->Init_Ctrl[11].bit[4] = 7; + state->Init_Ctrl[11].val[4] = 0; + + state->Init_Ctrl[12].Ctrl_Num = IF_VCO_BIAS ; + state->Init_Ctrl[12].size = 6 ; + state->Init_Ctrl[12].addr[0] = 44; + state->Init_Ctrl[12].bit[0] = 2; + state->Init_Ctrl[12].val[0] = 0; + state->Init_Ctrl[12].addr[1] = 44; + state->Init_Ctrl[12].bit[1] = 3; + state->Init_Ctrl[12].val[1] = 0; + state->Init_Ctrl[12].addr[2] = 44; + state->Init_Ctrl[12].bit[2] = 4; + state->Init_Ctrl[12].val[2] = 0; + state->Init_Ctrl[12].addr[3] = 44; + state->Init_Ctrl[12].bit[3] = 5; + state->Init_Ctrl[12].val[3] = 1; + state->Init_Ctrl[12].addr[4] = 44; + state->Init_Ctrl[12].bit[4] = 6; + state->Init_Ctrl[12].val[4] = 0; + state->Init_Ctrl[12].addr[5] = 44; + state->Init_Ctrl[12].bit[5] = 7; + state->Init_Ctrl[12].val[5] = 0; + + state->Init_Ctrl[13].Ctrl_Num = CHCAL_INT_MOD_IF ; + state->Init_Ctrl[13].size = 7 ; + state->Init_Ctrl[13].addr[0] = 11; + state->Init_Ctrl[13].bit[0] = 0; + state->Init_Ctrl[13].val[0] = 1; + state->Init_Ctrl[13].addr[1] = 11; + state->Init_Ctrl[13].bit[1] = 1; + state->Init_Ctrl[13].val[1] = 0; + state->Init_Ctrl[13].addr[2] = 11; + state->Init_Ctrl[13].bit[2] = 2; + state->Init_Ctrl[13].val[2] = 0; + state->Init_Ctrl[13].addr[3] = 11; + state->Init_Ctrl[13].bit[3] = 3; + state->Init_Ctrl[13].val[3] = 1; + state->Init_Ctrl[13].addr[4] = 11; + state->Init_Ctrl[13].bit[4] = 4; + state->Init_Ctrl[13].val[4] = 1; + state->Init_Ctrl[13].addr[5] = 11; + state->Init_Ctrl[13].bit[5] = 5; + state->Init_Ctrl[13].val[5] = 0; + state->Init_Ctrl[13].addr[6] = 11; + state->Init_Ctrl[13].bit[6] = 6; + state->Init_Ctrl[13].val[6] = 0; + + state->Init_Ctrl[14].Ctrl_Num = CHCAL_FRAC_MOD_IF ; + state->Init_Ctrl[14].size = 16 ; + state->Init_Ctrl[14].addr[0] = 13; + state->Init_Ctrl[14].bit[0] = 0; + state->Init_Ctrl[14].val[0] = 0; + state->Init_Ctrl[14].addr[1] = 13; + state->Init_Ctrl[14].bit[1] = 1; + state->Init_Ctrl[14].val[1] = 0; + state->Init_Ctrl[14].addr[2] = 13; + state->Init_Ctrl[14].bit[2] = 2; + state->Init_Ctrl[14].val[2] = 0; + state->Init_Ctrl[14].addr[3] = 13; + state->Init_Ctrl[14].bit[3] = 3; + state->Init_Ctrl[14].val[3] = 0; + state->Init_Ctrl[14].addr[4] = 13; + state->Init_Ctrl[14].bit[4] = 4; + state->Init_Ctrl[14].val[4] = 0; + state->Init_Ctrl[14].addr[5] = 13; + state->Init_Ctrl[14].bit[5] = 5; + state->Init_Ctrl[14].val[5] = 0; + state->Init_Ctrl[14].addr[6] = 13; + state->Init_Ctrl[14].bit[6] = 6; + state->Init_Ctrl[14].val[6] = 0; + state->Init_Ctrl[14].addr[7] = 13; + state->Init_Ctrl[14].bit[7] = 7; + state->Init_Ctrl[14].val[7] = 0; + state->Init_Ctrl[14].addr[8] = 12; + state->Init_Ctrl[14].bit[8] = 0; + state->Init_Ctrl[14].val[8] = 0; + state->Init_Ctrl[14].addr[9] = 12; + state->Init_Ctrl[14].bit[9] = 1; + state->Init_Ctrl[14].val[9] = 0; + state->Init_Ctrl[14].addr[10] = 12; + state->Init_Ctrl[14].bit[10] = 2; + state->Init_Ctrl[14].val[10] = 0; + state->Init_Ctrl[14].addr[11] = 12; + state->Init_Ctrl[14].bit[11] = 3; + state->Init_Ctrl[14].val[11] = 0; + state->Init_Ctrl[14].addr[12] = 12; + state->Init_Ctrl[14].bit[12] = 4; + state->Init_Ctrl[14].val[12] = 0; + state->Init_Ctrl[14].addr[13] = 12; + state->Init_Ctrl[14].bit[13] = 5; + state->Init_Ctrl[14].val[13] = 1; + state->Init_Ctrl[14].addr[14] = 12; + state->Init_Ctrl[14].bit[14] = 6; + state->Init_Ctrl[14].val[14] = 1; + state->Init_Ctrl[14].addr[15] = 12; + state->Init_Ctrl[14].bit[15] = 7; + state->Init_Ctrl[14].val[15] = 0; + + state->Init_Ctrl[15].Ctrl_Num = DRV_RES_SEL ; + state->Init_Ctrl[15].size = 3 ; + state->Init_Ctrl[15].addr[0] = 147; + state->Init_Ctrl[15].bit[0] = 2; + state->Init_Ctrl[15].val[0] = 0; + state->Init_Ctrl[15].addr[1] = 147; + state->Init_Ctrl[15].bit[1] = 3; + state->Init_Ctrl[15].val[1] = 1; + state->Init_Ctrl[15].addr[2] = 147; + state->Init_Ctrl[15].bit[2] = 4; + state->Init_Ctrl[15].val[2] = 1; + + state->Init_Ctrl[16].Ctrl_Num = I_DRIVER ; + state->Init_Ctrl[16].size = 2 ; + state->Init_Ctrl[16].addr[0] = 147; + state->Init_Ctrl[16].bit[0] = 0; + state->Init_Ctrl[16].val[0] = 0; + state->Init_Ctrl[16].addr[1] = 147; + state->Init_Ctrl[16].bit[1] = 1; + state->Init_Ctrl[16].val[1] = 1; + + state->Init_Ctrl[17].Ctrl_Num = EN_AAF ; + state->Init_Ctrl[17].size = 1 ; + state->Init_Ctrl[17].addr[0] = 147; + state->Init_Ctrl[17].bit[0] = 7; + state->Init_Ctrl[17].val[0] = 0; + + state->Init_Ctrl[18].Ctrl_Num = EN_3P ; + state->Init_Ctrl[18].size = 1 ; + state->Init_Ctrl[18].addr[0] = 147; + state->Init_Ctrl[18].bit[0] = 6; + state->Init_Ctrl[18].val[0] = 0; + + state->Init_Ctrl[19].Ctrl_Num = EN_AUX_3P ; + state->Init_Ctrl[19].size = 1 ; + state->Init_Ctrl[19].addr[0] = 156; + state->Init_Ctrl[19].bit[0] = 0; + state->Init_Ctrl[19].val[0] = 0; + + state->Init_Ctrl[20].Ctrl_Num = SEL_AAF_BAND ; + state->Init_Ctrl[20].size = 1 ; + state->Init_Ctrl[20].addr[0] = 147; + state->Init_Ctrl[20].bit[0] = 5; + state->Init_Ctrl[20].val[0] = 0; + + state->Init_Ctrl[21].Ctrl_Num = SEQ_ENCLK16_CLK_OUT ; + state->Init_Ctrl[21].size = 1 ; + state->Init_Ctrl[21].addr[0] = 137; + state->Init_Ctrl[21].bit[0] = 4; + state->Init_Ctrl[21].val[0] = 0; + + state->Init_Ctrl[22].Ctrl_Num = SEQ_SEL4_16B ; + state->Init_Ctrl[22].size = 1 ; + state->Init_Ctrl[22].addr[0] = 137; + state->Init_Ctrl[22].bit[0] = 7; + state->Init_Ctrl[22].val[0] = 0; + + state->Init_Ctrl[23].Ctrl_Num = XTAL_CAPSELECT ; + state->Init_Ctrl[23].size = 1 ; + state->Init_Ctrl[23].addr[0] = 91; + state->Init_Ctrl[23].bit[0] = 5; + state->Init_Ctrl[23].val[0] = 1; + + state->Init_Ctrl[24].Ctrl_Num = IF_SEL_DBL ; + state->Init_Ctrl[24].size = 1 ; + state->Init_Ctrl[24].addr[0] = 43; + state->Init_Ctrl[24].bit[0] = 0; + state->Init_Ctrl[24].val[0] = 1; + + state->Init_Ctrl[25].Ctrl_Num = RFSYN_R_DIV ; + state->Init_Ctrl[25].size = 2 ; + state->Init_Ctrl[25].addr[0] = 22; + state->Init_Ctrl[25].bit[0] = 0; + state->Init_Ctrl[25].val[0] = 1; + state->Init_Ctrl[25].addr[1] = 22; + state->Init_Ctrl[25].bit[1] = 1; + state->Init_Ctrl[25].val[1] = 1; + + state->Init_Ctrl[26].Ctrl_Num = SEQ_EXTSYNTHCALIF ; + state->Init_Ctrl[26].size = 1 ; + state->Init_Ctrl[26].addr[0] = 134; + state->Init_Ctrl[26].bit[0] = 2; + state->Init_Ctrl[26].val[0] = 0; + + state->Init_Ctrl[27].Ctrl_Num = SEQ_EXTDCCAL ; + state->Init_Ctrl[27].size = 1 ; + state->Init_Ctrl[27].addr[0] = 137; + state->Init_Ctrl[27].bit[0] = 3; + state->Init_Ctrl[27].val[0] = 0; + + state->Init_Ctrl[28].Ctrl_Num = AGC_EN_RSSI ; + state->Init_Ctrl[28].size = 1 ; + state->Init_Ctrl[28].addr[0] = 77; + state->Init_Ctrl[28].bit[0] = 7; + state->Init_Ctrl[28].val[0] = 0; + + state->Init_Ctrl[29].Ctrl_Num = RFA_ENCLKRFAGC ; + state->Init_Ctrl[29].size = 1 ; + state->Init_Ctrl[29].addr[0] = 166; + state->Init_Ctrl[29].bit[0] = 7; + state->Init_Ctrl[29].val[0] = 1; + + state->Init_Ctrl[30].Ctrl_Num = RFA_RSSI_REFH ; + state->Init_Ctrl[30].size = 3 ; + state->Init_Ctrl[30].addr[0] = 166; + state->Init_Ctrl[30].bit[0] = 0; + state->Init_Ctrl[30].val[0] = 0; + state->Init_Ctrl[30].addr[1] = 166; + state->Init_Ctrl[30].bit[1] = 1; + state->Init_Ctrl[30].val[1] = 1; + state->Init_Ctrl[30].addr[2] = 166; + state->Init_Ctrl[30].bit[2] = 2; + state->Init_Ctrl[30].val[2] = 1; + + state->Init_Ctrl[31].Ctrl_Num = RFA_RSSI_REF ; + state->Init_Ctrl[31].size = 3 ; + state->Init_Ctrl[31].addr[0] = 166; + state->Init_Ctrl[31].bit[0] = 3; + state->Init_Ctrl[31].val[0] = 1; + state->Init_Ctrl[31].addr[1] = 166; + state->Init_Ctrl[31].bit[1] = 4; + state->Init_Ctrl[31].val[1] = 0; + state->Init_Ctrl[31].addr[2] = 166; + state->Init_Ctrl[31].bit[2] = 5; + state->Init_Ctrl[31].val[2] = 1; + + state->Init_Ctrl[32].Ctrl_Num = RFA_RSSI_REFL ; + state->Init_Ctrl[32].size = 3 ; + state->Init_Ctrl[32].addr[0] = 167; + state->Init_Ctrl[32].bit[0] = 0; + state->Init_Ctrl[32].val[0] = 1; + state->Init_Ctrl[32].addr[1] = 167; + state->Init_Ctrl[32].bit[1] = 1; + state->Init_Ctrl[32].val[1] = 1; + state->Init_Ctrl[32].addr[2] = 167; + state->Init_Ctrl[32].bit[2] = 2; + state->Init_Ctrl[32].val[2] = 0; + + state->Init_Ctrl[33].Ctrl_Num = RFA_FLR ; + state->Init_Ctrl[33].size = 4 ; + state->Init_Ctrl[33].addr[0] = 168; + state->Init_Ctrl[33].bit[0] = 0; + state->Init_Ctrl[33].val[0] = 0; + state->Init_Ctrl[33].addr[1] = 168; + state->Init_Ctrl[33].bit[1] = 1; + state->Init_Ctrl[33].val[1] = 1; + state->Init_Ctrl[33].addr[2] = 168; + state->Init_Ctrl[33].bit[2] = 2; + state->Init_Ctrl[33].val[2] = 0; + state->Init_Ctrl[33].addr[3] = 168; + state->Init_Ctrl[33].bit[3] = 3; + state->Init_Ctrl[33].val[3] = 0; + + state->Init_Ctrl[34].Ctrl_Num = RFA_CEIL ; + state->Init_Ctrl[34].size = 4 ; + state->Init_Ctrl[34].addr[0] = 168; + state->Init_Ctrl[34].bit[0] = 4; + state->Init_Ctrl[34].val[0] = 1; + state->Init_Ctrl[34].addr[1] = 168; + state->Init_Ctrl[34].bit[1] = 5; + state->Init_Ctrl[34].val[1] = 1; + state->Init_Ctrl[34].addr[2] = 168; + state->Init_Ctrl[34].bit[2] = 6; + state->Init_Ctrl[34].val[2] = 1; + state->Init_Ctrl[34].addr[3] = 168; + state->Init_Ctrl[34].bit[3] = 7; + state->Init_Ctrl[34].val[3] = 1; + + state->Init_Ctrl[35].Ctrl_Num = SEQ_EXTIQFSMPULSE ; + state->Init_Ctrl[35].size = 1 ; + state->Init_Ctrl[35].addr[0] = 135; + state->Init_Ctrl[35].bit[0] = 0; + state->Init_Ctrl[35].val[0] = 0; + + state->Init_Ctrl[36].Ctrl_Num = OVERRIDE_1 ; + state->Init_Ctrl[36].size = 1 ; + state->Init_Ctrl[36].addr[0] = 56; + state->Init_Ctrl[36].bit[0] = 3; + state->Init_Ctrl[36].val[0] = 0; + + state->Init_Ctrl[37].Ctrl_Num = BB_INITSTATE_DLPF_TUNE ; + state->Init_Ctrl[37].size = 7 ; + state->Init_Ctrl[37].addr[0] = 59; + state->Init_Ctrl[37].bit[0] = 1; + state->Init_Ctrl[37].val[0] = 0; + state->Init_Ctrl[37].addr[1] = 59; + state->Init_Ctrl[37].bit[1] = 2; + state->Init_Ctrl[37].val[1] = 0; + state->Init_Ctrl[37].addr[2] = 59; + state->Init_Ctrl[37].bit[2] = 3; + state->Init_Ctrl[37].val[2] = 0; + state->Init_Ctrl[37].addr[3] = 59; + state->Init_Ctrl[37].bit[3] = 4; + state->Init_Ctrl[37].val[3] = 0; + state->Init_Ctrl[37].addr[4] = 59; + state->Init_Ctrl[37].bit[4] = 5; + state->Init_Ctrl[37].val[4] = 0; + state->Init_Ctrl[37].addr[5] = 59; + state->Init_Ctrl[37].bit[5] = 6; + state->Init_Ctrl[37].val[5] = 0; + state->Init_Ctrl[37].addr[6] = 59; + state->Init_Ctrl[37].bit[6] = 7; + state->Init_Ctrl[37].val[6] = 0; + + state->Init_Ctrl[38].Ctrl_Num = TG_R_DIV ; + state->Init_Ctrl[38].size = 6 ; + state->Init_Ctrl[38].addr[0] = 32; + state->Init_Ctrl[38].bit[0] = 2; + state->Init_Ctrl[38].val[0] = 0; + state->Init_Ctrl[38].addr[1] = 32; + state->Init_Ctrl[38].bit[1] = 3; + state->Init_Ctrl[38].val[1] = 0; + state->Init_Ctrl[38].addr[2] = 32; + state->Init_Ctrl[38].bit[2] = 4; + state->Init_Ctrl[38].val[2] = 0; + state->Init_Ctrl[38].addr[3] = 32; + state->Init_Ctrl[38].bit[3] = 5; + state->Init_Ctrl[38].val[3] = 0; + state->Init_Ctrl[38].addr[4] = 32; + state->Init_Ctrl[38].bit[4] = 6; + state->Init_Ctrl[38].val[4] = 1; + state->Init_Ctrl[38].addr[5] = 32; + state->Init_Ctrl[38].bit[5] = 7; + state->Init_Ctrl[38].val[5] = 0; + + state->Init_Ctrl[39].Ctrl_Num = EN_CHP_LIN_B ; + state->Init_Ctrl[39].size = 1 ; + state->Init_Ctrl[39].addr[0] = 25; + state->Init_Ctrl[39].bit[0] = 3; + state->Init_Ctrl[39].val[0] = 1; + + + state->CH_Ctrl_Num = CHCTRL_NUM ; + + state->CH_Ctrl[0].Ctrl_Num = DN_POLY ; + state->CH_Ctrl[0].size = 2 ; + state->CH_Ctrl[0].addr[0] = 68; + state->CH_Ctrl[0].bit[0] = 6; + state->CH_Ctrl[0].val[0] = 1; + state->CH_Ctrl[0].addr[1] = 68; + state->CH_Ctrl[0].bit[1] = 7; + state->CH_Ctrl[0].val[1] = 1; + + state->CH_Ctrl[1].Ctrl_Num = DN_RFGAIN ; + state->CH_Ctrl[1].size = 2 ; + state->CH_Ctrl[1].addr[0] = 70; + state->CH_Ctrl[1].bit[0] = 6; + state->CH_Ctrl[1].val[0] = 1; + state->CH_Ctrl[1].addr[1] = 70; + state->CH_Ctrl[1].bit[1] = 7; + state->CH_Ctrl[1].val[1] = 0; + + state->CH_Ctrl[2].Ctrl_Num = DN_CAP_RFLPF ; + state->CH_Ctrl[2].size = 9 ; + state->CH_Ctrl[2].addr[0] = 69; + state->CH_Ctrl[2].bit[0] = 5; + state->CH_Ctrl[2].val[0] = 0; + state->CH_Ctrl[2].addr[1] = 69; + state->CH_Ctrl[2].bit[1] = 6; + state->CH_Ctrl[2].val[1] = 0; + state->CH_Ctrl[2].addr[2] = 69; + state->CH_Ctrl[2].bit[2] = 7; + state->CH_Ctrl[2].val[2] = 0; + state->CH_Ctrl[2].addr[3] = 68; + state->CH_Ctrl[2].bit[3] = 0; + state->CH_Ctrl[2].val[3] = 0; + state->CH_Ctrl[2].addr[4] = 68; + state->CH_Ctrl[2].bit[4] = 1; + state->CH_Ctrl[2].val[4] = 0; + state->CH_Ctrl[2].addr[5] = 68; + state->CH_Ctrl[2].bit[5] = 2; + state->CH_Ctrl[2].val[5] = 0; + state->CH_Ctrl[2].addr[6] = 68; + state->CH_Ctrl[2].bit[6] = 3; + state->CH_Ctrl[2].val[6] = 0; + state->CH_Ctrl[2].addr[7] = 68; + state->CH_Ctrl[2].bit[7] = 4; + state->CH_Ctrl[2].val[7] = 0; + state->CH_Ctrl[2].addr[8] = 68; + state->CH_Ctrl[2].bit[8] = 5; + state->CH_Ctrl[2].val[8] = 0; + + state->CH_Ctrl[3].Ctrl_Num = DN_EN_VHFUHFBAR ; + state->CH_Ctrl[3].size = 1 ; + state->CH_Ctrl[3].addr[0] = 70; + state->CH_Ctrl[3].bit[0] = 5; + state->CH_Ctrl[3].val[0] = 0; + + state->CH_Ctrl[4].Ctrl_Num = DN_GAIN_ADJUST ; + state->CH_Ctrl[4].size = 3 ; + state->CH_Ctrl[4].addr[0] = 73; + state->CH_Ctrl[4].bit[0] = 4; + state->CH_Ctrl[4].val[0] = 0; + state->CH_Ctrl[4].addr[1] = 73; + state->CH_Ctrl[4].bit[1] = 5; + state->CH_Ctrl[4].val[1] = 1; + state->CH_Ctrl[4].addr[2] = 73; + state->CH_Ctrl[4].bit[2] = 6; + state->CH_Ctrl[4].val[2] = 0; + + state->CH_Ctrl[5].Ctrl_Num = DN_IQTNBUF_AMP ; + state->CH_Ctrl[5].size = 4 ; + state->CH_Ctrl[5].addr[0] = 70; + state->CH_Ctrl[5].bit[0] = 0; + state->CH_Ctrl[5].val[0] = 0; + state->CH_Ctrl[5].addr[1] = 70; + state->CH_Ctrl[5].bit[1] = 1; + state->CH_Ctrl[5].val[1] = 0; + state->CH_Ctrl[5].addr[2] = 70; + state->CH_Ctrl[5].bit[2] = 2; + state->CH_Ctrl[5].val[2] = 0; + state->CH_Ctrl[5].addr[3] = 70; + state->CH_Ctrl[5].bit[3] = 3; + state->CH_Ctrl[5].val[3] = 0; + + state->CH_Ctrl[6].Ctrl_Num = DN_IQTNGNBFBIAS_BST ; + state->CH_Ctrl[6].size = 1 ; + state->CH_Ctrl[6].addr[0] = 70; + state->CH_Ctrl[6].bit[0] = 4; + state->CH_Ctrl[6].val[0] = 1; + + state->CH_Ctrl[7].Ctrl_Num = RFSYN_EN_OUTMUX ; + state->CH_Ctrl[7].size = 1 ; + state->CH_Ctrl[7].addr[0] = 111; + state->CH_Ctrl[7].bit[0] = 4; + state->CH_Ctrl[7].val[0] = 0; + + state->CH_Ctrl[8].Ctrl_Num = RFSYN_SEL_VCO_OUT ; + state->CH_Ctrl[8].size = 1 ; + state->CH_Ctrl[8].addr[0] = 111; + state->CH_Ctrl[8].bit[0] = 7; + state->CH_Ctrl[8].val[0] = 1; + + state->CH_Ctrl[9].Ctrl_Num = RFSYN_SEL_VCO_HI ; + state->CH_Ctrl[9].size = 1 ; + state->CH_Ctrl[9].addr[0] = 111; + state->CH_Ctrl[9].bit[0] = 6; + state->CH_Ctrl[9].val[0] = 1; + + state->CH_Ctrl[10].Ctrl_Num = RFSYN_SEL_DIVM ; + state->CH_Ctrl[10].size = 1 ; + state->CH_Ctrl[10].addr[0] = 111; + state->CH_Ctrl[10].bit[0] = 5; + state->CH_Ctrl[10].val[0] = 0; + + state->CH_Ctrl[11].Ctrl_Num = RFSYN_RF_DIV_BIAS ; + state->CH_Ctrl[11].size = 2 ; + state->CH_Ctrl[11].addr[0] = 110; + state->CH_Ctrl[11].bit[0] = 0; + state->CH_Ctrl[11].val[0] = 1; + state->CH_Ctrl[11].addr[1] = 110; + state->CH_Ctrl[11].bit[1] = 1; + state->CH_Ctrl[11].val[1] = 0; + + state->CH_Ctrl[12].Ctrl_Num = DN_SEL_FREQ ; + state->CH_Ctrl[12].size = 3 ; + state->CH_Ctrl[12].addr[0] = 69; + state->CH_Ctrl[12].bit[0] = 2; + state->CH_Ctrl[12].val[0] = 0; + state->CH_Ctrl[12].addr[1] = 69; + state->CH_Ctrl[12].bit[1] = 3; + state->CH_Ctrl[12].val[1] = 0; + state->CH_Ctrl[12].addr[2] = 69; + state->CH_Ctrl[12].bit[2] = 4; + state->CH_Ctrl[12].val[2] = 0; + + state->CH_Ctrl[13].Ctrl_Num = RFSYN_VCO_BIAS ; + state->CH_Ctrl[13].size = 6 ; + state->CH_Ctrl[13].addr[0] = 110; + state->CH_Ctrl[13].bit[0] = 2; + state->CH_Ctrl[13].val[0] = 0; + state->CH_Ctrl[13].addr[1] = 110; + state->CH_Ctrl[13].bit[1] = 3; + state->CH_Ctrl[13].val[1] = 0; + state->CH_Ctrl[13].addr[2] = 110; + state->CH_Ctrl[13].bit[2] = 4; + state->CH_Ctrl[13].val[2] = 0; + state->CH_Ctrl[13].addr[3] = 110; + state->CH_Ctrl[13].bit[3] = 5; + state->CH_Ctrl[13].val[3] = 0; + state->CH_Ctrl[13].addr[4] = 110; + state->CH_Ctrl[13].bit[4] = 6; + state->CH_Ctrl[13].val[4] = 0; + state->CH_Ctrl[13].addr[5] = 110; + state->CH_Ctrl[13].bit[5] = 7; + state->CH_Ctrl[13].val[5] = 1; + + state->CH_Ctrl[14].Ctrl_Num = CHCAL_INT_MOD_RF ; + state->CH_Ctrl[14].size = 7 ; + state->CH_Ctrl[14].addr[0] = 14; + state->CH_Ctrl[14].bit[0] = 0; + state->CH_Ctrl[14].val[0] = 0; + state->CH_Ctrl[14].addr[1] = 14; + state->CH_Ctrl[14].bit[1] = 1; + state->CH_Ctrl[14].val[1] = 0; + state->CH_Ctrl[14].addr[2] = 14; + state->CH_Ctrl[14].bit[2] = 2; + state->CH_Ctrl[14].val[2] = 0; + state->CH_Ctrl[14].addr[3] = 14; + state->CH_Ctrl[14].bit[3] = 3; + state->CH_Ctrl[14].val[3] = 0; + state->CH_Ctrl[14].addr[4] = 14; + state->CH_Ctrl[14].bit[4] = 4; + state->CH_Ctrl[14].val[4] = 0; + state->CH_Ctrl[14].addr[5] = 14; + state->CH_Ctrl[14].bit[5] = 5; + state->CH_Ctrl[14].val[5] = 0; + state->CH_Ctrl[14].addr[6] = 14; + state->CH_Ctrl[14].bit[6] = 6; + state->CH_Ctrl[14].val[6] = 0; + + state->CH_Ctrl[15].Ctrl_Num = CHCAL_FRAC_MOD_RF ; + state->CH_Ctrl[15].size = 18 ; + state->CH_Ctrl[15].addr[0] = 17; + state->CH_Ctrl[15].bit[0] = 6; + state->CH_Ctrl[15].val[0] = 0; + state->CH_Ctrl[15].addr[1] = 17; + state->CH_Ctrl[15].bit[1] = 7; + state->CH_Ctrl[15].val[1] = 0; + state->CH_Ctrl[15].addr[2] = 16; + state->CH_Ctrl[15].bit[2] = 0; + state->CH_Ctrl[15].val[2] = 0; + state->CH_Ctrl[15].addr[3] = 16; + state->CH_Ctrl[15].bit[3] = 1; + state->CH_Ctrl[15].val[3] = 0; + state->CH_Ctrl[15].addr[4] = 16; + state->CH_Ctrl[15].bit[4] = 2; + state->CH_Ctrl[15].val[4] = 0; + state->CH_Ctrl[15].addr[5] = 16; + state->CH_Ctrl[15].bit[5] = 3; + state->CH_Ctrl[15].val[5] = 0; + state->CH_Ctrl[15].addr[6] = 16; + state->CH_Ctrl[15].bit[6] = 4; + state->CH_Ctrl[15].val[6] = 0; + state->CH_Ctrl[15].addr[7] = 16; + state->CH_Ctrl[15].bit[7] = 5; + state->CH_Ctrl[15].val[7] = 0; + state->CH_Ctrl[15].addr[8] = 16; + state->CH_Ctrl[15].bit[8] = 6; + state->CH_Ctrl[15].val[8] = 0; + state->CH_Ctrl[15].addr[9] = 16; + state->CH_Ctrl[15].bit[9] = 7; + state->CH_Ctrl[15].val[9] = 0; + state->CH_Ctrl[15].addr[10] = 15; + state->CH_Ctrl[15].bit[10] = 0; + state->CH_Ctrl[15].val[10] = 0; + state->CH_Ctrl[15].addr[11] = 15; + state->CH_Ctrl[15].bit[11] = 1; + state->CH_Ctrl[15].val[11] = 0; + state->CH_Ctrl[15].addr[12] = 15; + state->CH_Ctrl[15].bit[12] = 2; + state->CH_Ctrl[15].val[12] = 0; + state->CH_Ctrl[15].addr[13] = 15; + state->CH_Ctrl[15].bit[13] = 3; + state->CH_Ctrl[15].val[13] = 0; + state->CH_Ctrl[15].addr[14] = 15; + state->CH_Ctrl[15].bit[14] = 4; + state->CH_Ctrl[15].val[14] = 0; + state->CH_Ctrl[15].addr[15] = 15; + state->CH_Ctrl[15].bit[15] = 5; + state->CH_Ctrl[15].val[15] = 0; + state->CH_Ctrl[15].addr[16] = 15; + state->CH_Ctrl[15].bit[16] = 6; + state->CH_Ctrl[15].val[16] = 1; + state->CH_Ctrl[15].addr[17] = 15; + state->CH_Ctrl[15].bit[17] = 7; + state->CH_Ctrl[15].val[17] = 1; + + state->CH_Ctrl[16].Ctrl_Num = RFSYN_LPF_R ; + state->CH_Ctrl[16].size = 5 ; + state->CH_Ctrl[16].addr[0] = 112; + state->CH_Ctrl[16].bit[0] = 0; + state->CH_Ctrl[16].val[0] = 0; + state->CH_Ctrl[16].addr[1] = 112; + state->CH_Ctrl[16].bit[1] = 1; + state->CH_Ctrl[16].val[1] = 0; + state->CH_Ctrl[16].addr[2] = 112; + state->CH_Ctrl[16].bit[2] = 2; + state->CH_Ctrl[16].val[2] = 0; + state->CH_Ctrl[16].addr[3] = 112; + state->CH_Ctrl[16].bit[3] = 3; + state->CH_Ctrl[16].val[3] = 0; + state->CH_Ctrl[16].addr[4] = 112; + state->CH_Ctrl[16].bit[4] = 4; + state->CH_Ctrl[16].val[4] = 1; + + state->CH_Ctrl[17].Ctrl_Num = CHCAL_EN_INT_RF ; + state->CH_Ctrl[17].size = 1 ; + state->CH_Ctrl[17].addr[0] = 14; + state->CH_Ctrl[17].bit[0] = 7; + state->CH_Ctrl[17].val[0] = 0; + + state->CH_Ctrl[18].Ctrl_Num = TG_LO_DIVVAL ; + state->CH_Ctrl[18].size = 4 ; + state->CH_Ctrl[18].addr[0] = 107; + state->CH_Ctrl[18].bit[0] = 3; + state->CH_Ctrl[18].val[0] = 0; + state->CH_Ctrl[18].addr[1] = 107; + state->CH_Ctrl[18].bit[1] = 4; + state->CH_Ctrl[18].val[1] = 0; + state->CH_Ctrl[18].addr[2] = 107; + state->CH_Ctrl[18].bit[2] = 5; + state->CH_Ctrl[18].val[2] = 0; + state->CH_Ctrl[18].addr[3] = 107; + state->CH_Ctrl[18].bit[3] = 6; + state->CH_Ctrl[18].val[3] = 0; + + state->CH_Ctrl[19].Ctrl_Num = TG_LO_SELVAL ; + state->CH_Ctrl[19].size = 3 ; + state->CH_Ctrl[19].addr[0] = 107; + state->CH_Ctrl[19].bit[0] = 7; + state->CH_Ctrl[19].val[0] = 1; + state->CH_Ctrl[19].addr[1] = 106; + state->CH_Ctrl[19].bit[1] = 0; + state->CH_Ctrl[19].val[1] = 1; + state->CH_Ctrl[19].addr[2] = 106; + state->CH_Ctrl[19].bit[2] = 1; + state->CH_Ctrl[19].val[2] = 1; + + state->CH_Ctrl[20].Ctrl_Num = TG_DIV_VAL ; + state->CH_Ctrl[20].size = 11 ; + state->CH_Ctrl[20].addr[0] = 109; + state->CH_Ctrl[20].bit[0] = 2; + state->CH_Ctrl[20].val[0] = 0; + state->CH_Ctrl[20].addr[1] = 109; + state->CH_Ctrl[20].bit[1] = 3; + state->CH_Ctrl[20].val[1] = 0; + state->CH_Ctrl[20].addr[2] = 109; + state->CH_Ctrl[20].bit[2] = 4; + state->CH_Ctrl[20].val[2] = 0; + state->CH_Ctrl[20].addr[3] = 109; + state->CH_Ctrl[20].bit[3] = 5; + state->CH_Ctrl[20].val[3] = 0; + state->CH_Ctrl[20].addr[4] = 109; + state->CH_Ctrl[20].bit[4] = 6; + state->CH_Ctrl[20].val[4] = 0; + state->CH_Ctrl[20].addr[5] = 109; + state->CH_Ctrl[20].bit[5] = 7; + state->CH_Ctrl[20].val[5] = 0; + state->CH_Ctrl[20].addr[6] = 108; + state->CH_Ctrl[20].bit[6] = 0; + state->CH_Ctrl[20].val[6] = 0; + state->CH_Ctrl[20].addr[7] = 108; + state->CH_Ctrl[20].bit[7] = 1; + state->CH_Ctrl[20].val[7] = 0; + state->CH_Ctrl[20].addr[8] = 108; + state->CH_Ctrl[20].bit[8] = 2; + state->CH_Ctrl[20].val[8] = 1; + state->CH_Ctrl[20].addr[9] = 108; + state->CH_Ctrl[20].bit[9] = 3; + state->CH_Ctrl[20].val[9] = 1; + state->CH_Ctrl[20].addr[10] = 108; + state->CH_Ctrl[20].bit[10] = 4; + state->CH_Ctrl[20].val[10] = 1; + + state->CH_Ctrl[21].Ctrl_Num = TG_VCO_BIAS ; + state->CH_Ctrl[21].size = 6 ; + state->CH_Ctrl[21].addr[0] = 106; + state->CH_Ctrl[21].bit[0] = 2; + state->CH_Ctrl[21].val[0] = 0; + state->CH_Ctrl[21].addr[1] = 106; + state->CH_Ctrl[21].bit[1] = 3; + state->CH_Ctrl[21].val[1] = 0; + state->CH_Ctrl[21].addr[2] = 106; + state->CH_Ctrl[21].bit[2] = 4; + state->CH_Ctrl[21].val[2] = 0; + state->CH_Ctrl[21].addr[3] = 106; + state->CH_Ctrl[21].bit[3] = 5; + state->CH_Ctrl[21].val[3] = 0; + state->CH_Ctrl[21].addr[4] = 106; + state->CH_Ctrl[21].bit[4] = 6; + state->CH_Ctrl[21].val[4] = 0; + state->CH_Ctrl[21].addr[5] = 106; + state->CH_Ctrl[21].bit[5] = 7; + state->CH_Ctrl[21].val[5] = 1; + + state->CH_Ctrl[22].Ctrl_Num = SEQ_EXTPOWERUP ; + state->CH_Ctrl[22].size = 1 ; + state->CH_Ctrl[22].addr[0] = 138; + state->CH_Ctrl[22].bit[0] = 4; + state->CH_Ctrl[22].val[0] = 1; + + state->CH_Ctrl[23].Ctrl_Num = OVERRIDE_2 ; + state->CH_Ctrl[23].size = 1 ; + state->CH_Ctrl[23].addr[0] = 17; + state->CH_Ctrl[23].bit[0] = 5; + state->CH_Ctrl[23].val[0] = 0; + + state->CH_Ctrl[24].Ctrl_Num = OVERRIDE_3 ; + state->CH_Ctrl[24].size = 1 ; + state->CH_Ctrl[24].addr[0] = 111; + state->CH_Ctrl[24].bit[0] = 3; + state->CH_Ctrl[24].val[0] = 0; + + state->CH_Ctrl[25].Ctrl_Num = OVERRIDE_4 ; + state->CH_Ctrl[25].size = 1 ; + state->CH_Ctrl[25].addr[0] = 112; + state->CH_Ctrl[25].bit[0] = 7; + state->CH_Ctrl[25].val[0] = 0; + + state->CH_Ctrl[26].Ctrl_Num = SEQ_FSM_PULSE ; + state->CH_Ctrl[26].size = 1 ; + state->CH_Ctrl[26].addr[0] = 136; + state->CH_Ctrl[26].bit[0] = 7; + state->CH_Ctrl[26].val[0] = 0; + + state->CH_Ctrl[27].Ctrl_Num = GPIO_4B ; + state->CH_Ctrl[27].size = 1 ; + state->CH_Ctrl[27].addr[0] = 149; + state->CH_Ctrl[27].bit[0] = 7; + state->CH_Ctrl[27].val[0] = 0; + + state->CH_Ctrl[28].Ctrl_Num = GPIO_3B ; + state->CH_Ctrl[28].size = 1 ; + state->CH_Ctrl[28].addr[0] = 149; + state->CH_Ctrl[28].bit[0] = 6; + state->CH_Ctrl[28].val[0] = 0; + + state->CH_Ctrl[29].Ctrl_Num = GPIO_4 ; + state->CH_Ctrl[29].size = 1 ; + state->CH_Ctrl[29].addr[0] = 149; + state->CH_Ctrl[29].bit[0] = 5; + state->CH_Ctrl[29].val[0] = 1; + + state->CH_Ctrl[30].Ctrl_Num = GPIO_3 ; + state->CH_Ctrl[30].size = 1 ; + state->CH_Ctrl[30].addr[0] = 149; + state->CH_Ctrl[30].bit[0] = 4; + state->CH_Ctrl[30].val[0] = 1; + + state->CH_Ctrl[31].Ctrl_Num = GPIO_1B ; + state->CH_Ctrl[31].size = 1 ; + state->CH_Ctrl[31].addr[0] = 149; + state->CH_Ctrl[31].bit[0] = 3; + state->CH_Ctrl[31].val[0] = 0; + + state->CH_Ctrl[32].Ctrl_Num = DAC_A_ENABLE ; + state->CH_Ctrl[32].size = 1 ; + state->CH_Ctrl[32].addr[0] = 93; + state->CH_Ctrl[32].bit[0] = 1; + state->CH_Ctrl[32].val[0] = 0; + + state->CH_Ctrl[33].Ctrl_Num = DAC_B_ENABLE ; + state->CH_Ctrl[33].size = 1 ; + state->CH_Ctrl[33].addr[0] = 93; + state->CH_Ctrl[33].bit[0] = 0; + state->CH_Ctrl[33].val[0] = 0; + + state->CH_Ctrl[34].Ctrl_Num = DAC_DIN_A ; + state->CH_Ctrl[34].size = 6 ; + state->CH_Ctrl[34].addr[0] = 92; + state->CH_Ctrl[34].bit[0] = 2; + state->CH_Ctrl[34].val[0] = 0; + state->CH_Ctrl[34].addr[1] = 92; + state->CH_Ctrl[34].bit[1] = 3; + state->CH_Ctrl[34].val[1] = 0; + state->CH_Ctrl[34].addr[2] = 92; + state->CH_Ctrl[34].bit[2] = 4; + state->CH_Ctrl[34].val[2] = 0; + state->CH_Ctrl[34].addr[3] = 92; + state->CH_Ctrl[34].bit[3] = 5; + state->CH_Ctrl[34].val[3] = 0; + state->CH_Ctrl[34].addr[4] = 92; + state->CH_Ctrl[34].bit[4] = 6; + state->CH_Ctrl[34].val[4] = 0; + state->CH_Ctrl[34].addr[5] = 92; + state->CH_Ctrl[34].bit[5] = 7; + state->CH_Ctrl[34].val[5] = 0; + + state->CH_Ctrl[35].Ctrl_Num = DAC_DIN_B ; + state->CH_Ctrl[35].size = 6 ; + state->CH_Ctrl[35].addr[0] = 93; + state->CH_Ctrl[35].bit[0] = 2; + state->CH_Ctrl[35].val[0] = 0; + state->CH_Ctrl[35].addr[1] = 93; + state->CH_Ctrl[35].bit[1] = 3; + state->CH_Ctrl[35].val[1] = 0; + state->CH_Ctrl[35].addr[2] = 93; + state->CH_Ctrl[35].bit[2] = 4; + state->CH_Ctrl[35].val[2] = 0; + state->CH_Ctrl[35].addr[3] = 93; + state->CH_Ctrl[35].bit[3] = 5; + state->CH_Ctrl[35].val[3] = 0; + state->CH_Ctrl[35].addr[4] = 93; + state->CH_Ctrl[35].bit[4] = 6; + state->CH_Ctrl[35].val[4] = 0; + state->CH_Ctrl[35].addr[5] = 93; + state->CH_Ctrl[35].bit[5] = 7; + state->CH_Ctrl[35].val[5] = 0; + +#ifdef _MXL_PRODUCTION + state->CH_Ctrl[36].Ctrl_Num = RFSYN_EN_DIV ; + state->CH_Ctrl[36].size = 1 ; + state->CH_Ctrl[36].addr[0] = 109; + state->CH_Ctrl[36].bit[0] = 1; + state->CH_Ctrl[36].val[0] = 1; + + state->CH_Ctrl[37].Ctrl_Num = RFSYN_DIVM ; + state->CH_Ctrl[37].size = 2 ; + state->CH_Ctrl[37].addr[0] = 112; + state->CH_Ctrl[37].bit[0] = 5; + state->CH_Ctrl[37].val[0] = 0; + state->CH_Ctrl[37].addr[1] = 112; + state->CH_Ctrl[37].bit[1] = 6; + state->CH_Ctrl[37].val[1] = 0; + + state->CH_Ctrl[38].Ctrl_Num = DN_BYPASS_AGC_I2C ; + state->CH_Ctrl[38].size = 1 ; + state->CH_Ctrl[38].addr[0] = 65; + state->CH_Ctrl[38].bit[0] = 1; + state->CH_Ctrl[38].val[0] = 0; +#endif + + return 0 ; +} + +static void InitTunerControls(struct dvb_frontend *fe) +{ + MXL5005_RegisterInit(fe); + MXL5005_ControlInit(fe); +#ifdef _MXL_INTERNAL + MXL5005_MXLControlInit(fe); +#endif +} + +static u16 MXL5005_TunerConfig(struct dvb_frontend *fe, + u8 Mode, /* 0: Analog Mode ; 1: Digital Mode */ + u8 IF_mode, /* for Analog Mode, 0: zero IF; 1: low IF */ + u32 Bandwidth, /* filter channel bandwidth (6, 7, 8) */ + u32 IF_out, /* Desired IF Out Frequency */ + u32 Fxtal, /* XTAL Frequency */ + u8 AGC_Mode, /* AGC Mode - Dual AGC: 0, Single AGC: 1 */ + u16 TOP, /* 0: Dual AGC; Value: take over point */ + u16 IF_OUT_LOAD, /* IF Out Load Resistor (200 / 300 Ohms) */ + u8 CLOCK_OUT, /* 0: turn off clk out; 1: turn on clock out */ + u8 DIV_OUT, /* 0: Div-1; 1: Div-4 */ + u8 CAPSELECT, /* 0: disable On-Chip pulling cap; 1: enable */ + u8 EN_RSSI, /* 0: disable RSSI; 1: enable RSSI */ + + /* Modulation Type; */ + /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ + u8 Mod_Type, + + /* Tracking Filter */ + /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ + u8 TF_Type + ) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + + state->Mode = Mode; + state->IF_Mode = IF_mode; + state->Chan_Bandwidth = Bandwidth; + state->IF_OUT = IF_out; + state->Fxtal = Fxtal; + state->AGC_Mode = AGC_Mode; + state->TOP = TOP; + state->IF_OUT_LOAD = IF_OUT_LOAD; + state->CLOCK_OUT = CLOCK_OUT; + state->DIV_OUT = DIV_OUT; + state->CAPSELECT = CAPSELECT; + state->EN_RSSI = EN_RSSI; + state->Mod_Type = Mod_Type; + state->TF_Type = TF_Type; + + /* Initialize all the controls and registers */ + InitTunerControls(fe); + + /* Synthesizer LO frequency calculation */ + MXL_SynthIFLO_Calc(fe); + + return status; +} + +static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + if (state->Mode == 1) /* Digital Mode */ + state->IF_LO = state->IF_OUT; + else /* Analog Mode */ { + if (state->IF_Mode == 0) /* Analog Zero IF mode */ + state->IF_LO = state->IF_OUT + 400000; + else /* Analog Low IF mode */ + state->IF_LO = state->IF_OUT + state->Chan_Bandwidth/2; + } +} + +static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + + if (state->Mode == 1) /* Digital Mode */ { + /* remove 20.48MHz setting for 2.6.10 */ + state->RF_LO = state->RF_IN; + /* change for 2.6.6 */ + state->TG_LO = state->RF_IN - 750000; + } else /* Analog Mode */ { + if (state->IF_Mode == 0) /* Analog Zero IF mode */ { + state->RF_LO = state->RF_IN - 400000; + state->TG_LO = state->RF_IN - 1750000; + } else /* Analog Low IF mode */ { + state->RF_LO = state->RF_IN - state->Chan_Bandwidth/2; + state->TG_LO = state->RF_IN - + state->Chan_Bandwidth + 500000; + } + } +} + +static u16 MXL_OverwriteICDefault(struct dvb_frontend *fe) +{ + u16 status = 0; + + status += MXL_ControlWrite(fe, OVERRIDE_1, 1); + status += MXL_ControlWrite(fe, OVERRIDE_2, 1); + status += MXL_ControlWrite(fe, OVERRIDE_3, 1); + status += MXL_ControlWrite(fe, OVERRIDE_4, 1); + + return status; +} + +static u16 MXL_BlockInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + + status += MXL_OverwriteICDefault(fe); + + /* Downconverter Control Dig Ana */ + status += MXL_ControlWrite(fe, DN_IQTN_AMP_CUT, state->Mode ? 1 : 0); + + /* Filter Control Dig Ana */ + status += MXL_ControlWrite(fe, BB_MODE, state->Mode ? 0 : 1); + status += MXL_ControlWrite(fe, BB_BUF, state->Mode ? 3 : 2); + status += MXL_ControlWrite(fe, BB_BUF_OA, state->Mode ? 1 : 0); + status += MXL_ControlWrite(fe, BB_IQSWAP, state->Mode ? 0 : 1); + status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 0); + + /* Initialize Low-Pass Filter */ + if (state->Mode) { /* Digital Mode */ + switch (state->Chan_Bandwidth) { + case 8000000: + status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 0); + break; + case 7000000: + status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2); + break; + case 6000000: + status += MXL_ControlWrite(fe, + BB_DLPF_BANDSEL, 3); + break; + } + } else { /* Analog Mode */ + switch (state->Chan_Bandwidth) { + case 8000000: /* Low Zero */ + status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, + (state->IF_Mode ? 0 : 3)); + break; + case 7000000: + status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, + (state->IF_Mode ? 1 : 4)); + break; + case 6000000: + status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, + (state->IF_Mode ? 2 : 5)); + break; + } + } + + /* Charge Pump Control Dig Ana */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, state->Mode ? 5 : 8); + status += MXL_ControlWrite(fe, + RFSYN_EN_CHP_HIGAIN, state->Mode ? 1 : 1); + status += MXL_ControlWrite(fe, EN_CHP_LIN_B, state->Mode ? 0 : 0); + + /* AGC TOP Control */ + if (state->AGC_Mode == 0) /* Dual AGC */ { + status += MXL_ControlWrite(fe, AGC_IF, 15); + status += MXL_ControlWrite(fe, AGC_RF, 15); + } else /* Single AGC Mode Dig Ana */ + status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12); + + if (state->TOP == 55) /* TOP == 5.5 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x0); + + if (state->TOP == 72) /* TOP == 7.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x1); + + if (state->TOP == 92) /* TOP == 9.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x2); + + if (state->TOP == 110) /* TOP == 11.0 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x3); + + if (state->TOP == 129) /* TOP == 12.9 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x4); + + if (state->TOP == 147) /* TOP == 14.7 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x5); + + if (state->TOP == 168) /* TOP == 16.8 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x6); + + if (state->TOP == 194) /* TOP == 19.4 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x7); + + if (state->TOP == 212) /* TOP == 21.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0x9); + + if (state->TOP == 232) /* TOP == 23.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xA); + + if (state->TOP == 252) /* TOP == 25.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xB); + + if (state->TOP == 271) /* TOP == 27.1 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xC); + + if (state->TOP == 292) /* TOP == 29.2 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xD); + + if (state->TOP == 317) /* TOP == 31.7 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xE); + + if (state->TOP == 349) /* TOP == 34.9 */ + status += MXL_ControlWrite(fe, AGC_IF, 0xF); + + /* IF Synthesizer Control */ + status += MXL_IFSynthInit(fe); + + /* IF UpConverter Control */ + if (state->IF_OUT_LOAD == 200) { + status += MXL_ControlWrite(fe, DRV_RES_SEL, 6); + status += MXL_ControlWrite(fe, I_DRIVER, 2); + } + if (state->IF_OUT_LOAD == 300) { + status += MXL_ControlWrite(fe, DRV_RES_SEL, 4); + status += MXL_ControlWrite(fe, I_DRIVER, 1); + } + + /* Anti-Alias Filtering Control + * initialise Anti-Aliasing Filter + */ + if (state->Mode) { /* Digital Mode */ + if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 6280000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 1); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); + } + if ((state->IF_OUT == 36125000UL) || + (state->IF_OUT == 36150000UL)) { + status += MXL_ControlWrite(fe, EN_AAF, 1); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); + } + if (state->IF_OUT > 36150000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 0); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); + } + } else { /* Analog Mode */ + if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 5000000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 1); + status += MXL_ControlWrite(fe, EN_3P, 1); + status += MXL_ControlWrite(fe, EN_AUX_3P, 1); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); + } + if (state->IF_OUT > 5000000UL) { + status += MXL_ControlWrite(fe, EN_AAF, 0); + status += MXL_ControlWrite(fe, EN_3P, 0); + status += MXL_ControlWrite(fe, EN_AUX_3P, 0); + status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); + } + } + + /* Demod Clock Out */ + if (state->CLOCK_OUT) + status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 1); + else + status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 0); + + if (state->DIV_OUT == 1) + status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 1); + if (state->DIV_OUT == 0) + status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 0); + + /* Crystal Control */ + if (state->CAPSELECT) + status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 1); + else + status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 0); + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) + status += MXL_ControlWrite(fe, IF_SEL_DBL, 1); + if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) + status += MXL_ControlWrite(fe, IF_SEL_DBL, 0); + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) + status += MXL_ControlWrite(fe, RFSYN_R_DIV, 3); + if (state->Fxtal > 22000000UL && state->Fxtal <= 32000000UL) + status += MXL_ControlWrite(fe, RFSYN_R_DIV, 0); + + /* Misc Controls */ + if (state->Mode == 0 && state->IF_Mode == 1) /* Analog LowIF mode */ + status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 0); + else + status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 1); + + /* status += MXL_ControlRead(fe, IF_DIVVAL, &IF_DIVVAL_Val); */ + + /* Set TG_R_DIV */ + status += MXL_ControlWrite(fe, TG_R_DIV, + MXL_Ceiling(state->Fxtal, 1000000)); + + /* Apply Default value to BB_INITSTATE_DLPF_TUNE */ + + /* RSSI Control */ + if (state->EN_RSSI) { + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); + + /* TOP point */ + status += MXL_ControlWrite(fe, RFA_FLR, 0); + status += MXL_ControlWrite(fe, RFA_CEIL, 12); + } + + /* Modulation type bit settings + * Override the control values preset + */ + if (state->Mod_Type == MXL_DVBT) /* DVB-T Mode */ { + state->AGC_Mode = 1; /* Single AGC Mode */ + + /* Enable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); + + /* TOP point */ + status += MXL_ControlWrite(fe, RFA_FLR, 2); + status += MXL_ControlWrite(fe, RFA_CEIL, 13); + if (state->IF_OUT <= 6280000UL) /* Low IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 0); + else /* High IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + + } + if (state->Mod_Type == MXL_ATSC) /* ATSC Mode */ { + state->AGC_Mode = 1; /* Single AGC Mode */ + + /* Enable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 4); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); + + /* TOP point */ + status += MXL_ControlWrite(fe, RFA_FLR, 2); + status += MXL_ControlWrite(fe, RFA_CEIL, 13); + status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 1); + /* Low Zero */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); + + if (state->IF_OUT <= 6280000UL) /* Low IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 0); + else /* High IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + } + if (state->Mod_Type == MXL_QAM) /* QAM Mode */ { + state->Mode = MXL_DIGITAL_MODE; + + /* state->AGC_Mode = 1; */ /* Single AGC Mode */ + + /* Disable RSSI */ /* change here for v2.6.5 */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); + /* change here for v2.6.5 */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + + if (state->IF_OUT <= 6280000UL) /* Low IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 0); + else /* High IF */ + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); +#if 1 + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); +#endif + + } + if (state->Mod_Type == MXL_ANALOG_CABLE) { + /* Analog Cable Mode */ + /* state->Mode = MXL_DIGITAL_MODE; */ + + state->AGC_Mode = 1; /* Single AGC Mode */ + + /* Disable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + /* change for 2.6.3 */ + status += MXL_ControlWrite(fe, AGC_IF, 1); + status += MXL_ControlWrite(fe, AGC_RF, 15); + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + } + + if (state->Mod_Type == MXL_ANALOG_OTA) { + /* Analog OTA Terrestrial mode add for 2.6.7 */ + /* state->Mode = MXL_ANALOG_MODE; */ + + /* Enable RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + status += MXL_ControlWrite(fe, BB_IQSWAP, 1); + } + + /* RSSI disable */ + if (state->EN_RSSI == 0) { + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + } + + return status; +} + +static u16 MXL_IFSynthInit(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0 ; + u32 Fref = 0 ; + u32 Kdbl, intModVal ; + u32 fracModVal ; + Kdbl = 2 ; + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) + Kdbl = 2 ; + if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) + Kdbl = 1 ; + + /* IF Synthesizer Control */ + if (state->Mode == 0 && state->IF_Mode == 1) /* Analog Low IF mode */ { + if (state->IF_LO == 41000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 328000000UL ; + } + if (state->IF_LO == 47000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 376000000UL ; + } + if (state->IF_LO == 54000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 324000000UL ; + } + if (state->IF_LO == 60000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 39250000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 314000000UL ; + } + if (state->IF_LO == 39650000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 317200000UL ; + } + if (state->IF_LO == 40150000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 321200000UL ; + } + if (state->IF_LO == 40650000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 325200000UL ; + } + } + + if (state->Mode || (state->Mode == 0 && state->IF_Mode == 0)) { + if (state->IF_LO == 57000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 342000000UL ; + } + if (state->IF_LO == 44000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352000000UL ; + } + if (state->IF_LO == 43750000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 350000000UL ; + } + if (state->IF_LO == 36650000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 366500000UL ; + } + if (state->IF_LO == 36150000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 361500000UL ; + } + if (state->IF_LO == 36000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 35250000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352500000UL ; + } + if (state->IF_LO == 34750000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 347500000UL ; + } + if (state->IF_LO == 6280000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 376800000UL ; + } + if (state->IF_LO == 5000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 4500000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 4570000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 365600000UL ; + } + if (state->IF_LO == 4000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 57400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 344400000UL ; + } + if (state->IF_LO == 44400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 355200000UL ; + } + if (state->IF_LO == 44150000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 353200000UL ; + } + if (state->IF_LO == 37050000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 370500000UL ; + } + if (state->IF_LO == 36550000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 365500000UL ; + } + if (state->IF_LO == 36125000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 361250000UL ; + } + if (state->IF_LO == 6000000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 360000000UL ; + } + if (state->IF_LO == 5400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 324000000UL ; + } + if (state->IF_LO == 5380000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); + Fref = 322800000UL ; + } + if (state->IF_LO == 5200000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 374400000UL ; + } + if (state->IF_LO == 4900000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352800000UL ; + } + if (state->IF_LO == 4400000UL) { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 352000000UL ; + } + if (state->IF_LO == 4063000UL) /* add for 2.6.8 */ { + status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); + status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); + Fref = 365670000UL ; + } + } + /* CHCAL_INT_MOD_IF */ + /* CHCAL_FRAC_MOD_IF */ + intModVal = Fref / (state->Fxtal * Kdbl/2); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_IF, intModVal); + + fracModVal = (2<<15)*(Fref/1000 - (state->Fxtal/1000 * Kdbl/2) * + intModVal); + + fracModVal = fracModVal / ((state->Fxtal * Kdbl/2)/1000); + status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_IF, fracModVal); + + return status ; +} + +static u32 MXL_GetXtalInt(u32 Xtal_Freq) +{ + if ((Xtal_Freq % 1000000) == 0) + return (Xtal_Freq / 10000); + else + return (((Xtal_Freq / 1000000) + 1)*100); +} + +static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + u32 divider_val, E3, E4, E5, E5A; + u32 Fmax, Fmin, FmaxBin, FminBin; + u32 Kdbl_RF = 2; + u32 tg_divval; + u32 tg_lo; + u32 Xtal_Int; + + u32 Fref_TG; + u32 Fvco; + + Xtal_Int = MXL_GetXtalInt(state->Fxtal); + + state->RF_IN = RF_Freq; + + MXL_SynthRFTGLO_Calc(fe); + + if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) + Kdbl_RF = 2; + if (state->Fxtal > 22000000 && state->Fxtal <= 32000000) + Kdbl_RF = 1; + + /* Downconverter Controls + * Look-Up Table Implementation for: + * DN_POLY + * DN_RFGAIN + * DN_CAP_RFLPF + * DN_EN_VHFUHFBAR + * DN_GAIN_ADJUST + * Change the boundary reference from RF_IN to RF_LO + */ + if (state->RF_LO < 40000000UL) + return -1; + + if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 2); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 423); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); + } + if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 222); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); + } + if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 147); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); + } + if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 9); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); + } + if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 3); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); + } + if (state->RF_LO > 300000000UL && state->RF_LO <= 650000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 1); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); + } + if (state->RF_LO > 650000000UL && state->RF_LO <= 900000000UL) { + status += MXL_ControlWrite(fe, DN_POLY, 3); + status += MXL_ControlWrite(fe, DN_RFGAIN, 2); + status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); + status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); + status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); + } + if (state->RF_LO > 900000000UL) + return -1; + + /* DN_IQTNBUF_AMP */ + /* DN_IQTNGNBFBIAS_BST */ + if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 300000000UL && state->RF_LO <= 400000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 400000000UL && state->RF_LO <= 450000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 450000000UL && state->RF_LO <= 500000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 500000000UL && state->RF_LO <= 550000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 550000000UL && state->RF_LO <= 600000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 600000000UL && state->RF_LO <= 650000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 650000000UL && state->RF_LO <= 700000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 700000000UL && state->RF_LO <= 750000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 750000000UL && state->RF_LO <= 800000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); + } + if (state->RF_LO > 800000000UL && state->RF_LO <= 850000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); + } + if (state->RF_LO > 850000000UL && state->RF_LO <= 900000000UL) { + status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); + status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); + } + + /* + * Set RF Synth and LO Path Control + * + * Look-Up table implementation for: + * RFSYN_EN_OUTMUX + * RFSYN_SEL_VCO_OUT + * RFSYN_SEL_VCO_HI + * RFSYN_SEL_DIVM + * RFSYN_RF_DIV_BIAS + * DN_SEL_FREQ + * + * Set divider_val, Fmax, Fmix to use in Equations + */ + FminBin = 28000000UL ; + FmaxBin = 42500000UL ; + if (state->RF_LO >= 40000000UL && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 64 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 42500000UL ; + FmaxBin = 56000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 64 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 56000000UL ; + FmaxBin = 85000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 32 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 85000000UL ; + FmaxBin = 112000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); + divider_val = 32 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 112000000UL ; + FmaxBin = 170000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); + divider_val = 16 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 170000000UL ; + FmaxBin = 225000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); + divider_val = 16 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 225000000UL ; + FmaxBin = 300000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 4); + divider_val = 8 ; + Fmax = 340000000UL ; + Fmin = FminBin ; + } + FminBin = 300000000UL ; + FmaxBin = 340000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 8 ; + Fmax = FmaxBin ; + Fmin = 225000000UL ; + } + FminBin = 340000000UL ; + FmaxBin = 450000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 2); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 8 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 450000000UL ; + FmaxBin = 680000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 4 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 680000000UL ; + FmaxBin = 900000000UL ; + if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + divider_val = 4 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + + /* CHCAL_INT_MOD_RF + * CHCAL_FRAC_MOD_RF + * RFSYN_LPF_R + * CHCAL_EN_INT_RF + */ + /* Equation E3 RFSYN_VCO_BIAS */ + E3 = (((Fmax-state->RF_LO)/1000)*32)/((Fmax-Fmin)/1000) + 8 ; + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, E3); + + /* Equation E4 CHCAL_INT_MOD_RF */ + E4 = (state->RF_LO*divider_val/1000)/(2*state->Fxtal*Kdbl_RF/1000); + MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, E4); + + /* Equation E5 CHCAL_FRAC_MOD_RF CHCAL_EN_INT_RF */ + E5 = ((2<<17)*(state->RF_LO/10000*divider_val - + (E4*(2*state->Fxtal*Kdbl_RF)/10000))) / + (2*state->Fxtal*Kdbl_RF/10000); + + status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); + + /* Equation E5A RFSYN_LPF_R */ + E5A = (((Fmax - state->RF_LO)/1000)*4/((Fmax-Fmin)/1000)) + 1 ; + status += MXL_ControlWrite(fe, RFSYN_LPF_R, E5A); + + /* Euqation E5B CHCAL_EN_INIT_RF */ + status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, ((E5 == 0) ? 1 : 0)); + /*if (E5 == 0) + * status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, 1); + *else + * status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); + */ + + /* + * Set TG Synth + * + * Look-Up table implementation for: + * TG_LO_DIVVAL + * TG_LO_SELVAL + * + * Set divider_val, Fmax, Fmix to use in Equations + */ + if (state->TG_LO < 33000000UL) + return -1; + + FminBin = 33000000UL ; + FmaxBin = 50000000UL ; + if (state->TG_LO >= FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x6); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); + divider_val = 36 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 50000000UL ; + FmaxBin = 67000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x1); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); + divider_val = 24 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 67000000UL ; + FmaxBin = 100000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0xC); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); + divider_val = 18 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 100000000UL ; + FmaxBin = 150000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); + divider_val = 12 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 150000000UL ; + FmaxBin = 200000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); + divider_val = 8 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 200000000UL ; + FmaxBin = 300000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); + divider_val = 6 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 300000000UL ; + FmaxBin = 400000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); + divider_val = 4 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 400000000UL ; + FmaxBin = 600000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); + divider_val = 3 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + FminBin = 600000000UL ; + FmaxBin = 900000000UL ; + if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { + status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); + status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); + divider_val = 2 ; + Fmax = FmaxBin ; + Fmin = FminBin ; + } + + /* TG_DIV_VAL */ + tg_divval = (state->TG_LO*divider_val/100000) * + (MXL_Ceiling(state->Fxtal, 1000000) * 100) / + (state->Fxtal/1000); + + status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval); + + if (state->TG_LO > 600000000UL) + status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval + 1); + + Fmax = 1800000000UL ; + Fmin = 1200000000UL ; + + /* prevent overflow of 32 bit unsigned integer, use + * following equation. Edit for v2.6.4 + */ + /* Fref_TF = Fref_TG * 1000 */ + Fref_TG = (state->Fxtal/1000) / MXL_Ceiling(state->Fxtal, 1000000); + + /* Fvco = Fvco/10 */ + Fvco = (state->TG_LO/10000) * divider_val * Fref_TG; + + tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8; + + /* below equation is same as above but much harder to debug. + * tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) - + * ((state->TG_LO/10000)*divider_val * + * (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 * + * Xtal_Int/100) + 8; + */ + + status += MXL_ControlWrite(fe, TG_VCO_BIAS , tg_lo); + + /* add for 2.6.5 Special setting for QAM */ + if (state->Mod_Type == MXL_QAM) { + if (state->RF_IN < 680000000) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + else + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); + } + + /* Off Chip Tracking Filter Control */ + if (state->TF_Type == MXL_TF_OFF) { + /* Tracking Filter Off State; turn off all the banks */ + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 3, 1); /* Bank1 Off */ + status += MXL_SetGPIO(fe, 1, 1); /* Bank2 Off */ + status += MXL_SetGPIO(fe, 4, 1); /* Bank3 Off */ + } + + if (state->TF_Type == MXL_TF_C) /* Tracking Filter type C */ { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_A, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 29); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 0); + } + if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 16); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 7); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + } + } + + if (state->TF_Type == MXL_TF_C_H) { + + /* Tracking Filter type C-H for Hauppauge only */ + status += MXL_ControlWrite(fe, DAC_DIN_A, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 0); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 0); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 0); + } + if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + } + } + + if (state->TF_Type == MXL_TF_D) { /* Tracking Filter type D */ + + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_D_L) { + + /* Tracking Filter type D-L for Lumanate ONLY change 2.6.3 */ + status += MXL_ControlWrite(fe, DAC_DIN_A, 0); + + /* if UHF and terrestrial => Turn off Tracking Filter */ + if (state->RF_IN >= 471000000 && + (state->RF_IN - 471000000)%6000000 != 0) { + /* Turn off all the banks */ + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_ControlWrite(fe, AGC_IF, 10); + } else { + /* if VHF or cable => Turn on Tracking Filter */ + if (state->RF_IN >= 43000000 && + state->RF_IN < 140000000) { + + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 140000000 && + state->RF_IN < 240000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 240000000 && + state->RF_IN < 340000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 340000000 && + state->RF_IN < 430000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 430000000 && + state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 470000000 && + state->RF_IN < 570000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 570000000 && + state->RF_IN < 620000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 620000000 && + state->RF_IN < 760000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 760000000 && + state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + } + + if (state->TF_Type == MXL_TF_E) /* Tracking Filter type E */ { + + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_F) { + + /* Tracking Filter type F */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 160000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 160000000 && state->RF_IN < 210000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 210000000 && state->RF_IN < 300000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 300000000 && state->RF_IN < 390000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 390000000 && state->RF_IN < 515000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 515000000 && state->RF_IN < 650000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 650000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_E_2) { + + /* Tracking Filter type E_2 */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_G) { + + /* Tracking Filter type G add for v2.6.8 */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + if (state->RF_IN >= 50000000 && state->RF_IN < 190000000) { + + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 190000000 && state->RF_IN < 280000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 280000000 && state->RF_IN < 350000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 400000000 && state->RF_IN < 470000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 640000000 && state->RF_IN < 820000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 820000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + + if (state->TF_Type == MXL_TF_E_NA) { + + /* Tracking Filter type E-NA for Empia ONLY change for 2.6.8 */ + status += MXL_ControlWrite(fe, DAC_DIN_B, 0); + + /* if UHF and terrestrial=> Turn off Tracking Filter */ + if (state->RF_IN >= 471000000 && + (state->RF_IN - 471000000)%6000000 != 0) { + + /* Turn off all the banks */ + status += MXL_SetGPIO(fe, 3, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + + /* 2.6.12 Turn on RSSI */ + status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); + status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); + status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); + + /* RSSI reference point */ + status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); + status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); + status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); + + /* following parameter is from analog OTA mode, + * can be change to seek better performance */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); + } else { + /* if VHF or Cable => Turn on Tracking Filter */ + + /* 2.6.12 Turn off RSSI */ + status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); + + /* change back from above condition */ + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); + + + if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { + + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 0); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 1); + } + if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 0); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 0); + } + if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { + status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); + status += MXL_SetGPIO(fe, 4, 1); + status += MXL_SetGPIO(fe, 1, 1); + status += MXL_SetGPIO(fe, 3, 1); + } + } + } + return status ; +} + +static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val) +{ + u16 status = 0; + + if (GPIO_Num == 1) + status += MXL_ControlWrite(fe, GPIO_1B, GPIO_Val ? 0 : 1); + + /* GPIO2 is not available */ + + if (GPIO_Num == 3) { + if (GPIO_Val == 1) { + status += MXL_ControlWrite(fe, GPIO_3, 0); + status += MXL_ControlWrite(fe, GPIO_3B, 0); + } + if (GPIO_Val == 0) { + status += MXL_ControlWrite(fe, GPIO_3, 1); + status += MXL_ControlWrite(fe, GPIO_3B, 1); + } + if (GPIO_Val == 3) { /* tri-state */ + status += MXL_ControlWrite(fe, GPIO_3, 0); + status += MXL_ControlWrite(fe, GPIO_3B, 1); + } + } + if (GPIO_Num == 4) { + if (GPIO_Val == 1) { + status += MXL_ControlWrite(fe, GPIO_4, 0); + status += MXL_ControlWrite(fe, GPIO_4B, 0); + } + if (GPIO_Val == 0) { + status += MXL_ControlWrite(fe, GPIO_4, 1); + status += MXL_ControlWrite(fe, GPIO_4B, 1); + } + if (GPIO_Val == 3) { /* tri-state */ + status += MXL_ControlWrite(fe, GPIO_4, 0); + status += MXL_ControlWrite(fe, GPIO_4B, 1); + } + } + + return status; +} + +static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value) +{ + u16 status = 0; + + /* Will write ALL Matching Control Name */ + /* Write Matching INIT Control */ + status += MXL_ControlWrite_Group(fe, ControlNum, value, 1); + /* Write Matching CH Control */ + status += MXL_ControlWrite_Group(fe, ControlNum, value, 2); +#ifdef _MXL_INTERNAL + /* Write Matching MXL Control */ + status += MXL_ControlWrite_Group(fe, ControlNum, value, 3); +#endif + return status; +} + +static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, + u32 value, u16 controlGroup) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 i, j, k; + u32 highLimit; + u32 ctrlVal; + + if (controlGroup == 1) /* Initial Control */ { + + for (i = 0; i < state->Init_Ctrl_Num; i++) { + + if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { + + highLimit = 1 << state->Init_Ctrl[i].size; + if (value < highLimit) { + for (j = 0; j < state->Init_Ctrl[i].size; j++) { + state->Init_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); + MXL_RegWriteBit(fe, (u8)(state->Init_Ctrl[i].addr[j]), + (u8)(state->Init_Ctrl[i].bit[j]), + (u8)((value>>j) & 0x01)); + } + ctrlVal = 0; + for (k = 0; k < state->Init_Ctrl[i].size; k++) + ctrlVal += state->Init_Ctrl[i].val[k] * (1 << k); + } else + return -1; + } + } + } + if (controlGroup == 2) /* Chan change Control */ { + + for (i = 0; i < state->CH_Ctrl_Num; i++) { + + if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { + + highLimit = 1 << state->CH_Ctrl[i].size; + if (value < highLimit) { + for (j = 0; j < state->CH_Ctrl[i].size; j++) { + state->CH_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); + MXL_RegWriteBit(fe, (u8)(state->CH_Ctrl[i].addr[j]), + (u8)(state->CH_Ctrl[i].bit[j]), + (u8)((value>>j) & 0x01)); + } + ctrlVal = 0; + for (k = 0; k < state->CH_Ctrl[i].size; k++) + ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); + } else + return -1; + } + } + } +#ifdef _MXL_INTERNAL + if (controlGroup == 3) /* Maxlinear Control */ { + + for (i = 0; i < state->MXL_Ctrl_Num; i++) { + + if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { + + highLimit = (1 << state->MXL_Ctrl[i].size); + if (value < highLimit) { + for (j = 0; j < state->MXL_Ctrl[i].size; j++) { + state->MXL_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); + MXL_RegWriteBit(fe, (u8)(state->MXL_Ctrl[i].addr[j]), + (u8)(state->MXL_Ctrl[i].bit[j]), + (u8)((value>>j) & 0x01)); + } + ctrlVal = 0; + for (k = 0; k < state->MXL_Ctrl[i].size; k++) + ctrlVal += state->MXL_Ctrl[i].val[k] * (1 << k); + } else + return -1; + } + } + } +#endif + return 0 ; /* successful return */ +} + +static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal) +{ + struct mxl5005s_state *state = fe->tuner_priv; + int i ; + + for (i = 0; i < 104; i++) { + if (RegNum == state->TunerRegs[i].Reg_Num) { + *RegVal = (u8)(state->TunerRegs[i].Reg_Val); + return 0; + } + } + + return 1; +} + +static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u32 ctrlVal ; + u16 i, k ; + + for (i = 0; i < state->Init_Ctrl_Num ; i++) { + + if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { + + ctrlVal = 0; + for (k = 0; k < state->Init_Ctrl[i].size; k++) + ctrlVal += state->Init_Ctrl[i].val[k] * (1<<k); + *value = ctrlVal; + return 0; + } + } + + for (i = 0; i < state->CH_Ctrl_Num ; i++) { + + if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { + + ctrlVal = 0; + for (k = 0; k < state->CH_Ctrl[i].size; k++) + ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); + *value = ctrlVal; + return 0; + + } + } + +#ifdef _MXL_INTERNAL + for (i = 0; i < state->MXL_Ctrl_Num ; i++) { + + if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { + + ctrlVal = 0; + for (k = 0; k < state->MXL_Ctrl[i].size; k++) + ctrlVal += state->MXL_Ctrl[i].val[k] * (1<<k); + *value = ctrlVal; + return 0; + + } + } +#endif + return 1; +} + +static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, + u8 bitVal) +{ + struct mxl5005s_state *state = fe->tuner_priv; + int i ; + + const u8 AND_MAP[8] = { + 0xFE, 0xFD, 0xFB, 0xF7, + 0xEF, 0xDF, 0xBF, 0x7F } ; + + const u8 OR_MAP[8] = { + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 } ; + + for (i = 0; i < state->TunerRegs_Num; i++) { + if (state->TunerRegs[i].Reg_Num == address) { + if (bitVal) + state->TunerRegs[i].Reg_Val |= OR_MAP[bit]; + else + state->TunerRegs[i].Reg_Val &= AND_MAP[bit]; + break ; + } + } +} + +static u32 MXL_Ceiling(u32 value, u32 resolution) +{ + return (value/resolution + (value % resolution > 0 ? 1 : 0)); +} + +/* Retrieve the Initialzation Registers */ +static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count) +{ + u16 status = 0; + int i ; + + u8 RegAddr[] = { + 11, 12, 13, 22, 32, 43, 44, 53, 56, 59, 73, + 76, 77, 91, 134, 135, 137, 147, + 156, 166, 167, 168, 25 }; + + *count = sizeof(RegAddr) / sizeof(u8); + + status += MXL_BlockInit(fe); + + for (i = 0 ; i < *count; i++) { + RegNum[i] = RegAddr[i]; + status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); + } + + return status; +} + +static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, + int *count) +{ + u16 status = 0; + int i ; + +/* add 77, 166, 167, 168 register for 2.6.12 */ +#ifdef _MXL_PRODUCTION + u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 65, 68, 69, 70, 73, 92, 93, 106, + 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; +#else + u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 68, 69, 70, 73, 92, 93, 106, + 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; + /* + u8 RegAddr[171]; + for (i = 0; i <= 170; i++) + RegAddr[i] = i; + */ +#endif + + *count = sizeof(RegAddr) / sizeof(u8); + + for (i = 0 ; i < *count; i++) { + RegNum[i] = RegAddr[i]; + status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); + } + + return status; +} + +static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, + u8 *RegVal, int *count) +{ + u16 status = 0; + int i; + + u8 RegAddr[] = {43, 136}; + + *count = sizeof(RegAddr) / sizeof(u8); + + for (i = 0; i < *count; i++) { + RegNum[i] = RegAddr[i]; + status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); + } + + return status; +} + +static u16 MXL_GetMasterControl(u8 *MasterReg, int state) +{ + if (state == 1) /* Load_Start */ + *MasterReg = 0xF3; + if (state == 2) /* Power_Down */ + *MasterReg = 0x41; + if (state == 3) /* Synth_Reset */ + *MasterReg = 0xB1; + if (state == 4) /* Seq_Off */ + *MasterReg = 0xF1; + + return 0; +} + +#ifdef _MXL_PRODUCTION +static u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0 ; + + if (VCO_Range == 1) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 180224); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 222822); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 229376); + } + } + + if (VCO_Range == 2) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 16384); + } + } + + if (VCO_Range == 3) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 173670); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 173670); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 245760); + } + } + + if (VCO_Range == 4) { + status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); + status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); + status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); + status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); + status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + if (state->Mode == 0 && state->IF_Mode == 1) { + /* Analog Low IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 0 && state->IF_Mode == 0) { + /* Analog Zero IF Mode */ + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 206438); + } + if (state->Mode == 1) /* Digital Mode */ { + status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); + status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); + status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); + status += MXL_ControlWrite(fe, + CHCAL_FRAC_MOD_RF, 212992); + } + } + + return status; +} + +static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u16 status = 0; + + if (Hystersis == 1) + status += MXL_ControlWrite(fe, DN_BYPASS_AGC_I2C, 1); + + return status; +} +#endif +/* End: Reference driver code found in the Realtek driver that + * is copyright MaxLinear */ + +/* ---------------------------------------------------------------- + * Begin: Everything after here is new code to adapt the + * proprietary Realtek driver into a Linux API tuner. + * Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + */ +static int mxl5005s_reset(struct dvb_frontend *fe) +{ + struct mxl5005s_state *state = fe->tuner_priv; + int ret = 0; + + u8 buf[2] = { 0xff, 0x00 }; + struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, + .buf = buf, .len = 2 }; + + dprintk(2, "%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mxl5005s I2C reset failed\n"); + ret = -EREMOTEIO; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* Write a single byte to a single reg, latch the value if required by + * following the transaction with the latch byte. + */ +static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE }; + struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, + .buf = buf, .len = 3 }; + + if (latch == 0) + msg.len = 2; + + dprintk(2, "%s(0x%x, 0x%x, 0x%x)\n", __func__, reg, val, msg.addr); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mxl5005s I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, + u8 *datatable, u8 len) +{ + int ret = 0, i; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + for (i = 0 ; i < len-1; i++) { + ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0); + if (ret < 0) + break; + } + + ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int mxl5005s_init(struct dvb_frontend *fe) +{ + dprintk(1, "%s()\n", __func__); + return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ); +} + +static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth) +{ + struct mxl5005s_state *state = fe->tuner_priv; + + u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + int TableLen; + + dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth); + + mxl5005s_reset(fe); + + /* Tuner initialization stage 0 */ + MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); + AddrTable[0] = MASTER_CONTROL_ADDR; + ByteTable[0] |= state->config->AgcMasterByte; + + mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); + + mxl5005s_AssignTunerMode(fe, mod_type, bandwidth); + + /* Tuner initialization stage 1 */ + MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen); + + mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + + return 0; +} + +static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, + u32 bandwidth) +{ + struct mxl5005s_state *state = fe->tuner_priv; + struct mxl5005s_config *c = state->config; + + InitTunerControls(fe); + + /* Set MxL5005S parameters. */ + MXL5005_TunerConfig( + fe, + c->mod_mode, + c->if_mode, + bandwidth, + c->if_freq, + c->xtal_freq, + c->agc_mode, + c->top, + c->output_load, + c->clock_out, + c->div_out, + c->cap_select, + c->rssi_enable, + mod_type, + c->tracking_filter); + + return 0; +} + +static int mxl5005s_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct mxl5005s_state *state = fe->tuner_priv; + u32 req_mode, req_bw = 0; + int ret; + + dprintk(1, "%s()\n", __func__); + + if (fe->ops.info.type == FE_ATSC) { + switch (params->u.vsb.modulation) { + case VSB_8: + req_mode = MXL_ATSC; break; + default: + case QAM_64: + case QAM_256: + case QAM_AUTO: + req_mode = MXL_QAM; break; + } + } else + req_mode = MXL_DVBT; + + /* Change tuner for new modulation type if reqd */ + if (req_mode != state->current_mode) { + switch (req_mode) { + case VSB_8: + case QAM_64: + case QAM_256: + case QAM_AUTO: + req_bw = MXL5005S_BANDWIDTH_6MHZ; + break; + default: + /* Assume DVB-T */ + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + req_bw = MXL5005S_BANDWIDTH_6MHZ; + break; + case BANDWIDTH_7_MHZ: + req_bw = MXL5005S_BANDWIDTH_7MHZ; + break; + case BANDWIDTH_AUTO: + case BANDWIDTH_8_MHZ: + req_bw = MXL5005S_BANDWIDTH_8MHZ; + break; + } + } + + state->current_mode = req_mode; + ret = mxl5005s_reconfigure(fe, req_mode, req_bw); + + } else + ret = 0; + + if (ret == 0) { + dprintk(1, "%s() freq=%d\n", __func__, params->frequency); + ret = mxl5005s_SetRfFreqHz(fe, params->frequency); + } + + return ret; +} + +static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl5005s_state *state = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); + + *frequency = state->RF_IN; + + return 0; +} + +static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mxl5005s_state *state = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); + + *bandwidth = state->Chan_Bandwidth; + + return 0; +} + +static int mxl5005s_release(struct dvb_frontend *fe) +{ + dprintk(1, "%s()\n", __func__); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops mxl5005s_tuner_ops = { + .info = { + .name = "MaxLinear MXL5005S", + .frequency_min = 48000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + + .release = mxl5005s_release, + .init = mxl5005s_init, + + .set_params = mxl5005s_set_params, + .get_frequency = mxl5005s_get_frequency, + .get_bandwidth = mxl5005s_get_bandwidth, +}; + +struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mxl5005s_config *config) +{ + struct mxl5005s_state *state = NULL; + dprintk(1, "%s()\n", __func__); + + state = kzalloc(sizeof(struct mxl5005s_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->frontend = fe; + state->config = config; + state->i2c = i2c; + state->current_mode = MXL_QAM; + + printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n", + config->i2c_address); + + memcpy(&fe->ops.tuner_ops, &mxl5005s_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = state; + return fe; +} +EXPORT_SYMBOL(mxl5005s_attach); + +MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/common/tuners/mxl5005s.h b/linux/drivers/media/common/tuners/mxl5005s.h new file mode 100644 index 000000000..396db150b --- /dev/null +++ b/linux/drivers/media/common/tuners/mxl5005s.h @@ -0,0 +1,131 @@ +/* + MaxLinear MXL5005S VSB/QAM/DVBT tuner driver + + Copyright (C) 2008 MaxLinear + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __MXL5005S_H +#define __MXL5005S_H + +#include <linux/i2c.h> +#include "dvb_frontend.h" + +struct mxl5005s_config { + + /* 7 bit i2c address */ + u8 i2c_address; + +#define IF_FREQ_4570000HZ 4570000 +#define IF_FREQ_4571429HZ 4571429 +#define IF_FREQ_5380000HZ 5380000 +#define IF_FREQ_36000000HZ 36000000 +#define IF_FREQ_36125000HZ 36125000 +#define IF_FREQ_36166667HZ 36166667 +#define IF_FREQ_44000000HZ 44000000 + u32 if_freq; + +#define CRYSTAL_FREQ_4000000HZ 4000000 +#define CRYSTAL_FREQ_16000000HZ 16000000 +#define CRYSTAL_FREQ_25000000HZ 25000000 +#define CRYSTAL_FREQ_28800000HZ 28800000 + u32 xtal_freq; + +#define MXL_DUAL_AGC 0 +#define MXL_SINGLE_AGC 1 + u8 agc_mode; + +#define MXL_TF_DEFAULT 0 +#define MXL_TF_OFF 1 +#define MXL_TF_C 2 +#define MXL_TF_C_H 3 +#define MXL_TF_D 4 +#define MXL_TF_D_L 5 +#define MXL_TF_E 6 +#define MXL_TF_F 7 +#define MXL_TF_E_2 8 +#define MXL_TF_E_NA 9 +#define MXL_TF_G 10 + u8 tracking_filter; + +#define MXL_RSSI_DISABLE 0 +#define MXL_RSSI_ENABLE 1 + u8 rssi_enable; + +#define MXL_CAP_SEL_DISABLE 0 +#define MXL_CAP_SEL_ENABLE 1 + u8 cap_select; + +#define MXL_DIV_OUT_1 0 +#define MXL_DIV_OUT_4 1 + u8 div_out; + +#define MXL_CLOCK_OUT_DISABLE 0 +#define MXL_CLOCK_OUT_ENABLE 1 + u8 clock_out; + +#define MXL5005S_IF_OUTPUT_LOAD_200_OHM 200 +#define MXL5005S_IF_OUTPUT_LOAD_300_OHM 300 + u32 output_load; + +#define MXL5005S_TOP_5P5 55 +#define MXL5005S_TOP_7P2 72 +#define MXL5005S_TOP_9P2 92 +#define MXL5005S_TOP_11P0 110 +#define MXL5005S_TOP_12P9 129 +#define MXL5005S_TOP_14P7 147 +#define MXL5005S_TOP_16P8 168 +#define MXL5005S_TOP_19P4 194 +#define MXL5005S_TOP_21P2 212 +#define MXL5005S_TOP_23P2 232 +#define MXL5005S_TOP_25P2 252 +#define MXL5005S_TOP_27P1 271 +#define MXL5005S_TOP_29P2 292 +#define MXL5005S_TOP_31P7 317 +#define MXL5005S_TOP_34P9 349 + u32 top; + +#define MXL_ANALOG_MODE 0 +#define MXL_DIGITAL_MODE 1 + u8 mod_mode; + +#define MXL_ZERO_IF 0 +#define MXL_LOW_IF 1 + u8 if_mode; + + /* Stuff I don't know what to do with */ + u8 AgcMasterByte; +}; + +#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || \ + (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE)) +extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mxl5005s_config *config); +#else +static inline struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct mxl5005s_config *config) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_TUNER_MXL5005S */ + +#endif /* __MXL5005S_H */ + diff --git a/linux/drivers/media/dvb/frontends/qt1010.c b/linux/drivers/media/common/tuners/qt1010.c index 4afed44ab..4afed44ab 100644 --- a/linux/drivers/media/dvb/frontends/qt1010.c +++ b/linux/drivers/media/common/tuners/qt1010.c diff --git a/linux/drivers/media/dvb/frontends/qt1010.h b/linux/drivers/media/common/tuners/qt1010.h index cff6a7ca5..807fb7b61 100644 --- a/linux/drivers/media/dvb/frontends/qt1010.h +++ b/linux/drivers/media/common/tuners/qt1010.h @@ -36,7 +36,7 @@ struct qt1010_config { * @param cfg tuner hw based configuration * @return fe pointer on success, NULL on failure */ -#if defined(CONFIG_DVB_TUNER_QT1010) || (defined(CONFIG_DVB_TUNER_QT1010_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE)) extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct qt1010_config *cfg); @@ -48,6 +48,6 @@ static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif // CONFIG_DVB_TUNER_QT1010 +#endif // CONFIG_MEDIA_TUNER_QT1010 #endif diff --git a/linux/drivers/media/dvb/frontends/qt1010_priv.h b/linux/drivers/media/common/tuners/qt1010_priv.h index 090cf475f..090cf475f 100644 --- a/linux/drivers/media/dvb/frontends/qt1010_priv.h +++ b/linux/drivers/media/common/tuners/qt1010_priv.h diff --git a/linux/drivers/media/dvb/frontends/tda18271-common.c b/linux/drivers/media/common/tuners/tda18271-common.c index 43c0bec83..5bdab8181 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-common.c +++ b/linux/drivers/media/common/tuners/tda18271-common.c @@ -227,9 +227,8 @@ int tda18271_charge_pump_source(struct dvb_frontend *fe, regs[r_cp] &= ~0x20; regs[r_cp] |= ((force & 1) << 5); - tda18271_write_regs(fe, r_cp, 1); - return 0; + return tda18271_write_regs(fe, r_cp, 1); } int tda18271_init_regs(struct dvb_frontend *fe) @@ -555,16 +554,15 @@ int tda18271_set_standby_mode(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; - tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt); + if (tda18271_debug & DBG_ADV) + tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt); regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */ regs[R_EP3] |= sm ? (1 << 7) : 0 | sm_lt ? (1 << 6) : 0 | sm_xt ? (1 << 5) : 0; - tda18271_write_regs(fe, R_EP3, 1); - - return 0; + return tda18271_write_regs(fe, R_EP3, 1); } /*---------------------------------------------------------------------*/ @@ -578,7 +576,7 @@ int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq) u32 div; int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_MPD] = (0x77 & pd); @@ -610,7 +608,7 @@ int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq) u32 div; int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_CPD] = pd; @@ -634,7 +632,7 @@ int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_EP1] &= ~0x07; /* clear bp filter bits */ @@ -651,7 +649,7 @@ int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_EB13] &= ~0x7c; /* clear k & m bits */ @@ -668,7 +666,7 @@ int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_EP2] &= ~0xe0; /* clear rf band bits */ @@ -685,7 +683,7 @@ int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ @@ -702,7 +700,7 @@ int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val); - if (ret < 0) + if (tda_fail(ret)) goto fail; regs[R_EP5] &= ~0x07; diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/common/tuners/tda18271-fe.c index 355c37a42..f9e2957bb 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/common/tuners/tda18271-fe.c @@ -52,6 +52,7 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; u32 N; /* update TV broadcast parameters */ @@ -86,7 +87,9 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, /* update rf top / if top */ regs[R_EB22] = 0x00; regs[R_EB22] |= map->rfagc_top; - tda18271_write_regs(fe, R_EB22, 1); + ret = tda18271_write_regs(fe, R_EB22, 1); + if (tda_fail(ret)) + goto fail; /* --------------------------------------------------------------- */ @@ -122,7 +125,9 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, /* agc1 has priority on agc2 */ regs[R_EB1] &= ~0x01; - tda18271_write_regs(fe, R_EB1, 1); + ret = tda18271_write_regs(fe, R_EB1, 1); + if (tda_fail(ret)) + goto fail; /* --------------------------------------------------------------- */ @@ -142,7 +147,9 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, break; } - tda18271_write_regs(fe, R_TM, 7); + ret = tda18271_write_regs(fe, R_TM, 7); + if (tda_fail(ret)) + goto fail; /* force charge pump source */ charge_pump_source(fe, 1); @@ -159,9 +166,9 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, regs[R_EP3] &= ~0x04; else regs[R_EP3] |= 0x04; - tda18271_write_regs(fe, R_EP3, 1); - - return 0; + ret = tda18271_write_regs(fe, R_EP3, 1); +fail: + return ret; } static int tda18271_read_thermometer(struct dvb_frontend *fe) @@ -214,11 +221,13 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - int tm_current, rfcal_comp, approx, i; + int tm_current, rfcal_comp, approx, i, ret; u8 dc_over_dt, rf_tab; /* power up */ - tda18271_set_standby_mode(fe, 0, 0, 0); + ret = tda18271_set_standby_mode(fe, 0, 0, 0); + if (tda_fail(ret)) + goto fail; /* read die current temperature */ tm_current = tda18271_read_thermometer(fe); @@ -229,8 +238,8 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, rf_tab = regs[R_EB14]; i = tda18271_lookup_rf_band(fe, &freq, NULL); - if (i < 0) - return -EINVAL; + if (tda_fail(i)) + return i; if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { approx = map[i].rf_a1 * @@ -251,35 +260,42 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal); regs[R_EB14] = approx + rfcal_comp; - tda18271_write_regs(fe, R_EB14, 1); - - return 0; + ret = tda18271_write_regs(fe, R_EB14, 1); +fail: + return ret; } static int tda18271_por(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; /* power up detector 1 */ regs[R_EB12] &= ~0x20; - tda18271_write_regs(fe, R_EB12, 1); + ret = tda18271_write_regs(fe, R_EB12, 1); + if (tda_fail(ret)) + goto fail; regs[R_EB18] &= ~0x80; /* turn agc1 loop on */ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ - tda18271_write_regs(fe, R_EB18, 1); + ret = tda18271_write_regs(fe, R_EB18, 1); + if (tda_fail(ret)) + goto fail; regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */ /* POR mode */ - tda18271_set_standby_mode(fe, 1, 0, 0); + ret = tda18271_set_standby_mode(fe, 1, 0, 0); + if (tda_fail(ret)) + goto fail; /* disable 1.5 MHz low pass filter */ regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */ regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */ - tda18271_write_regs(fe, R_EB21, 3); - - return 0; + ret = tda18271_write_regs(fe, R_EB21, 3); +fail: + return ret; } static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq) @@ -390,7 +406,7 @@ static int tda18271_powerscan(struct dvb_frontend *fe, { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; - int sgn, bcal, count, wait; + int sgn, bcal, count, wait, ret; u8 cid_target; u16 count_limit; u32 freq; @@ -422,7 +438,9 @@ static int tda18271_powerscan(struct dvb_frontend *fe, tda18271_write_regs(fe, R_EP2, 1); /* read power detection info, stored in EB10 */ - tda18271_read_extended(fe); + ret = tda18271_read_extended(fe); + if (tda_fail(ret)) + return ret; /* algorithm initialization */ sgn = 1; @@ -448,7 +466,9 @@ static int tda18271_powerscan(struct dvb_frontend *fe, tda18271_write_regs(fe, R_EP2, 1); /* read power detection info, stored in EB10 */ - tda18271_read_extended(fe); + ret = tda18271_read_extended(fe); + if (tda_fail(ret)) + return ret; count += 200; @@ -479,6 +499,7 @@ static int tda18271_powerscan_init(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; /* set standard to digital */ regs[R_EP3] &= ~0x1f; /* clear std bits */ @@ -490,10 +511,14 @@ static int tda18271_powerscan_init(struct dvb_frontend *fe) /* update IF output level & IF notch frequency */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ - tda18271_write_regs(fe, R_EP3, 2); + ret = tda18271_write_regs(fe, R_EP3, 2); + if (tda_fail(ret)) + goto fail; regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ - tda18271_write_regs(fe, R_EB18, 1); + ret = tda18271_write_regs(fe, R_EB18, 1); + if (tda_fail(ret)) + goto fail; regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */ @@ -501,9 +526,9 @@ static int tda18271_powerscan_init(struct dvb_frontend *fe) regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */ regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */ - tda18271_write_regs(fe, R_EB21, 3); - - return 0; + ret = tda18271_write_regs(fe, R_EB21, 3); +fail: + return ret; } static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) @@ -522,7 +547,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) i = tda18271_lookup_rf_band(fe, &freq, NULL); - if (i < 0) + if (tda_fail(i)) return i; rf_default[RF1] = 1000 * map[i].rf1_def; @@ -536,6 +561,8 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) /* look for optimized calibration frequency */ bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]); + if (tda_fail(bcal)) + return bcal; tda18271_calc_rf_cal(fe, &rf_freq[rf]); prog_tab[rf] = regs[R_EB14]; @@ -576,22 +603,29 @@ static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned int i; + int ret; tda_info("tda18271: performing RF tracking filter calibration\n"); /* wait for die temperature stabilization */ msleep(200); - tda18271_powerscan_init(fe); + ret = tda18271_powerscan_init(fe); + if (tda_fail(ret)) + goto fail; /* rf band calibration */ - for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) + for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) { + ret = tda18271_rf_tracking_filters_init(fe, 1000 * priv->rf_cal_state[i].rfmax); + if (tda_fail(ret)) + goto fail; + } priv->tm_rfcal = tda18271_read_thermometer(fe); - - return 0; +fail: + return ret; } /* ------------------------------------------------------------------ */ @@ -600,6 +634,7 @@ static int tda18271c2_rf_cal_init(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; /* test RF_CAL_OK to see if we need init */ if ((regs[R_EP1] & 0x10) == 0) @@ -608,15 +643,22 @@ static int tda18271c2_rf_cal_init(struct dvb_frontend *fe) if (priv->cal_initialized) return 0; - tda18271_calc_rf_filter_curve(fe); + ret = tda18271_calc_rf_filter_curve(fe); + if (tda_fail(ret)) + goto fail; - tda18271_por(fe); + ret = tda18271_por(fe); + if (tda_fail(ret)) + goto fail; tda_info("tda18271: RF tracking filter calibration complete\n"); priv->cal_initialized = true; - - return 0; + goto end; +fail: + tda_info("tda18271: RF tracking filter calibration failed!\n"); +end: + return ret; } static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe, @@ -624,6 +666,7 @@ static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe, { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; u32 N = 0; /* calculate bp filter */ @@ -672,7 +715,10 @@ static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe, tda18271_calc_main_pll(fe, N); - tda18271_write_regs(fe, R_EP3, 11); + ret = tda18271_write_regs(fe, R_EP3, 11); + if (tda_fail(ret)) + return ret; + msleep(5); /* RF tracking filter calibration initialization */ /* search for K,M,CO for RF calibration */ @@ -720,45 +766,56 @@ static int tda18271_ir_cal_init(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; - tda18271_read_regs(fe); + ret = tda18271_read_regs(fe); + if (tda_fail(ret)) + goto fail; /* test IR_CAL_OK to see if we need init */ if ((regs[R_EP1] & 0x08) == 0) - tda18271_init_regs(fe); - - return 0; + ret = tda18271_init_regs(fe); +fail: + return ret; } static int tda18271_init(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; + int ret; mutex_lock(&priv->lock); /* power up */ - tda18271_set_standby_mode(fe, 0, 0, 0); + ret = tda18271_set_standby_mode(fe, 0, 0, 0); + if (tda_fail(ret)) + goto fail; /* initialization */ - tda18271_ir_cal_init(fe); + ret = tda18271_ir_cal_init(fe); + if (tda_fail(ret)) + goto fail; if (priv->id == TDA18271HDC2) tda18271c2_rf_cal_init(fe); - +fail: mutex_unlock(&priv->lock); - return 0; + return ret; } static int tda18271_tune(struct dvb_frontend *fe, struct tda18271_std_map_item *map, u32 freq, u32 bw) { struct tda18271_priv *priv = fe->tuner_priv; + int ret; tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n", freq, map->if_freq, bw, map->agc_mode, map->std); - tda18271_init(fe); + ret = tda18271_init(fe); + if (tda_fail(ret)) + goto fail; mutex_lock(&priv->lock); @@ -770,11 +827,11 @@ static int tda18271_tune(struct dvb_frontend *fe, tda18271c2_rf_tracking_filters_correction(fe, freq); break; } - tda18271_channel_configuration(fe, map, freq, bw); + ret = tda18271_channel_configuration(fe, map, freq, bw); mutex_unlock(&priv->lock); - - return 0; +fail: + return ret; } /* ------------------------------------------------------------------ */ @@ -838,7 +895,7 @@ static int tda18271_set_params(struct dvb_frontend *fe, ret = tda18271_tune(fe, map, freq, bw); - if (ret < 0) + if (tda_fail(ret)) goto fail; priv->frequency = freq; @@ -894,7 +951,7 @@ static int tda18271_set_analog_params(struct dvb_frontend *fe, ret = tda18271_tune(fe, map, freq, 0); - if (ret < 0) + if (tda_fail(ret)) goto fail; priv->frequency = freq; @@ -906,16 +963,17 @@ fail: static int tda18271_sleep(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; + int ret; mutex_lock(&priv->lock); /* standby mode w/ slave tuner output * & loop thru & xtal oscillator on */ - tda18271_set_standby_mode(fe, 1, 0, 0); + ret = tda18271_set_standby_mode(fe, 1, 0, 0); mutex_unlock(&priv->lock); - return 0; + return ret; } static int tda18271_release(struct dvb_frontend *fe) @@ -1096,10 +1154,10 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, if (cfg) priv->small_i2c = cfg->small_i2c; - if (tda18271_get_id(fe) < 0) + if (tda_fail(tda18271_get_id(fe))) goto fail; - if (tda18271_assign_map_layout(fe) < 0) + if (tda_fail(tda18271_assign_map_layout(fe))) goto fail; mutex_lock(&priv->lock); diff --git a/linux/drivers/media/dvb/frontends/tda18271-tables.c b/linux/drivers/media/common/tuners/tda18271-maps.c index 83e756196..83e756196 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-tables.c +++ b/linux/drivers/media/common/tuners/tda18271-maps.c diff --git a/linux/drivers/media/dvb/frontends/tda18271-priv.h b/linux/drivers/media/common/tuners/tda18271-priv.h index 8ceeca081..2ed61a31f 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-priv.h +++ b/linux/drivers/media/common/tuners/tda18271-priv.h @@ -162,6 +162,15 @@ extern int tda18271_debug; #define tda_reg(fmt, arg...) dprintk(KERN_DEBUG, DBG_REG, fmt, ##arg) #define tda_cal(fmt, arg...) dprintk(KERN_DEBUG, DBG_CAL, fmt, ##arg) +#define tda_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\ + __ret; \ +}) + /*---------------------------------------------------------------------*/ enum tda18271_map_type { diff --git a/linux/drivers/media/dvb/frontends/tda18271.h b/linux/drivers/media/common/tuners/tda18271.h index 0e7af8d05..7db9831c0 100644 --- a/linux/drivers/media/dvb/frontends/tda18271.h +++ b/linux/drivers/media/common/tuners/tda18271.h @@ -81,7 +81,7 @@ struct tda18271_config { unsigned int small_i2c:1; }; -#if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE)) extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct i2c_adapter *i2c, struct tda18271_config *cfg); diff --git a/linux/drivers/media/dvb/frontends/tda827x.c b/linux/drivers/media/common/tuners/tda827x.c index 2d39f9be5..2d39f9be5 100644 --- a/linux/drivers/media/dvb/frontends/tda827x.c +++ b/linux/drivers/media/common/tuners/tda827x.c diff --git a/linux/drivers/media/dvb/frontends/tda827x.h b/linux/drivers/media/common/tuners/tda827x.h index b73c23570..7850a9a1d 100644 --- a/linux/drivers/media/dvb/frontends/tda827x.h +++ b/linux/drivers/media/common/tuners/tda827x.h @@ -51,7 +51,7 @@ struct tda827x_config * @param cfg optional callback function pointers. * @return FE pointer on success, NULL on failure. */ -#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE)) extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, struct tda827x_config *cfg); @@ -64,6 +64,6 @@ static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif // CONFIG_DVB_TDA827X +#endif // CONFIG_MEDIA_TUNER_TDA827X #endif // __DVB_TDA827X_H__ diff --git a/linux/drivers/media/video/tda8290.c b/linux/drivers/media/common/tuners/tda8290.c index d57602d22..9bda53be5 100644 --- a/linux/drivers/media/video/tda8290.c +++ b/linux/drivers/media/common/tuners/tda8290.c @@ -604,16 +604,16 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) if ((data == 0x83) || (data == 0x84)) { priv->ver |= TDA18271; - tda18271_attach(fe, priv->tda827x_addr, - priv->i2c_props.adap, - &tda829x_tda18271_config); + dvb_attach(tda18271_attach, fe, priv->tda827x_addr, + priv->i2c_props.adap, &tda829x_tda18271_config); } else { if ((data & 0x3c) == 0) priv->ver |= TDA8275; else priv->ver |= TDA8275A; - tda827x_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, &priv->cfg); + dvb_attach(tda827x_attach, fe, priv->tda827x_addr, + priv->i2c_props.adap, &priv->cfg); priv->cfg.switch_addr = priv->i2c_props.addr; } if (fe->ops.tuner_ops.init) diff --git a/linux/drivers/media/video/tda8290.h b/linux/drivers/media/common/tuners/tda8290.h index d3bbf276a..aa074f3f0 100644 --- a/linux/drivers/media/video/tda8290.h +++ b/linux/drivers/media/common/tuners/tda8290.h @@ -29,7 +29,7 @@ struct tda829x_config { #define TDA829X_DONT_PROBE 1 }; -#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE)) extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, diff --git a/linux/drivers/media/video/tda9887.c b/linux/drivers/media/common/tuners/tda9887.c index e98de7d40..e98de7d40 100644 --- a/linux/drivers/media/video/tda9887.c +++ b/linux/drivers/media/common/tuners/tda9887.c diff --git a/linux/drivers/media/video/tda9887.h b/linux/drivers/media/common/tuners/tda9887.h index be49dcbfc..acc419e8c 100644 --- a/linux/drivers/media/video/tda9887.h +++ b/linux/drivers/media/common/tuners/tda9887.h @@ -21,7 +21,7 @@ #include "dvb_frontend.h" /* ------------------------------------------------------------------------ */ -#if defined(CONFIG_TUNER_TDA9887) || (defined(CONFIG_TUNER_TDA9887_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE)) extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr); diff --git a/linux/drivers/media/video/tea5761.c b/linux/drivers/media/common/tuners/tea5761.c index fa1d12d6f..f5fadcd4b 100644 --- a/linux/drivers/media/video/tea5761.c +++ b/linux/drivers/media/common/tuners/tea5761.c @@ -248,14 +248,19 @@ int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) { printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc); - return EINVAL; + return -EINVAL; } - if (!((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061))) { - printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x. It is not a TEA5761\n",buffer[13],buffer[14],buffer[15]); - return EINVAL; + if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) { + printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x." + " It is not a TEA5761\n", + buffer[13], buffer[14], buffer[15]); + return -EINVAL; } - printk(KERN_WARNING "TEA5761 detected.\n"); + printk(KERN_WARNING "tea5761: TEA%02x%02x detected. " + "Manufacturer ID= 0x%02x\n", + buffer[14], buffer[15], buffer[13]); + return 0; } @@ -296,7 +301,7 @@ struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, { struct tea5761_priv *priv = NULL; - if (tea5761_autodetection(i2c_adap, i2c_addr) == EINVAL) + if (tea5761_autodetection(i2c_adap, i2c_addr) != 0) return NULL; priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL); diff --git a/linux/drivers/media/video/tea5761.h b/linux/drivers/media/common/tuners/tea5761.h index 8eb62722b..2e2ff82c9 100644 --- a/linux/drivers/media/video/tea5761.h +++ b/linux/drivers/media/common/tuners/tea5761.h @@ -20,7 +20,7 @@ #include <linux/i2c.h> #include "dvb_frontend.h" -#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, diff --git a/linux/drivers/media/video/tea5767.c b/linux/drivers/media/common/tuners/tea5767.c index c4f39a5d3..5990a63ef 100644 --- a/linux/drivers/media/video/tea5767.c +++ b/linux/drivers/media/common/tuners/tea5767.c @@ -380,14 +380,14 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); - return EINVAL; + return -EINVAL; } /* If all bytes are the same then it's a TV tuner and not a tea5767 */ if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && buffer[0] == buffer[3] && buffer[0] == buffer[4]) { printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); - return EINVAL; + return -EINVAL; } /* Status bytes: @@ -397,14 +397,16 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) */ if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); - return EINVAL; + return -EINVAL; } +#if 0 /* Not working for TEA5767 in Beholder Columbus card */ /* It seems that tea5767 returns 0xff after the 5th byte */ if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { printk(KERN_WARNING "Returned more than 5 bytes. It is not a TEA5767\n"); - return EINVAL; + return -EINVAL; } +#endif #if 0 /*Sometimes, this code doesn't work */ /* Sets tuner at some freq (87.5 MHz) and see if it is ok */ @@ -423,7 +425,7 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) if (5 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 5))) { printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); - return EINVAL; + return -EINVAL; } /* Initial freq for 32.768KHz clock */ @@ -431,7 +433,7 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) printk(KERN_WARNING "It is not a TEA5767. div=%d, Return: %02x %02x %02x %02x %02x\n", div,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4]); tea5767_status_dump(buffer); - return EINVAL; + return -EINVAL; } #endif return 0; @@ -488,8 +490,8 @@ struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, struct tea5767_priv *priv = NULL; #if 0 /* By removing autodetection allows forcing TEA chip */ - if (tea5767_autodetection(i2c_adap, i2c_addr) == EINVAL) - return EINVAL; + if (tea5767_autodetection(i2c_adap, i2c_addr) == -EINVAL) + return -EINVAL; #endif priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); if (priv == NULL) diff --git a/linux/drivers/media/video/tea5767.h b/linux/drivers/media/common/tuners/tea5767.h index 7b547c092..d30ab1b48 100644 --- a/linux/drivers/media/video/tea5767.h +++ b/linux/drivers/media/common/tuners/tea5767.h @@ -39,7 +39,7 @@ struct tea5767_ctrl { enum tea5767_xtal xtal_freq; }; -#if defined(CONFIG_TUNER_TEA5767) || (defined(CONFIG_TUNER_TEA5767_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE)) extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, diff --git a/linux/drivers/media/video/tuner-i2c.h b/linux/drivers/media/common/tuners/tuner-i2c.h index cb1c7141f..cb1c7141f 100644 --- a/linux/drivers/media/video/tuner-i2c.h +++ b/linux/drivers/media/common/tuners/tuner-i2c.h diff --git a/linux/drivers/media/video/tuner-simple.c b/linux/drivers/media/common/tuners/tuner-simple.c index 29c14a4c6..c9d70e4b2 100644 --- a/linux/drivers/media/video/tuner-simple.c +++ b/linux/drivers/media/common/tuners/tuner-simple.c @@ -1053,8 +1053,10 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); if (1 != i2c_transfer(i2c_adap, &msg, 1)) - tuner_warn("unable to probe %s, proceeding anyway.", - tuners[type].name); + printk(KERN_WARNING "tuner-simple %d-%04x: " + "unable to probe %s, proceeding anyway.", + i2c_adapter_id(i2c_adap), i2c_addr, + tuners[type].name); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); diff --git a/linux/drivers/media/video/tuner-simple.h b/linux/drivers/media/common/tuners/tuner-simple.h index e46cf0121..381fa5d35 100644 --- a/linux/drivers/media/video/tuner-simple.h +++ b/linux/drivers/media/common/tuners/tuner-simple.h @@ -20,7 +20,7 @@ #include <linux/i2c.h> #include "dvb_frontend.h" -#if defined(CONFIG_TUNER_SIMPLE) || (defined(CONFIG_TUNER_SIMPLE_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE)) extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr, diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/common/tuners/tuner-types.c index b3f0f62e0..b3f0f62e0 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/common/tuners/tuner-types.c diff --git a/linux/drivers/media/video/tuner-xc2028-types.h b/linux/drivers/media/common/tuners/tuner-xc2028-types.h index d0057fbf0..216665c2c 100644 --- a/linux/drivers/media/video/tuner-xc2028-types.h +++ b/linux/drivers/media/common/tuners/tuner-xc2028-types.h @@ -1,6 +1,9 @@ /* tuner-xc2028_types * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * This file includes internal tipes to be used inside tuner-xc2028. + * Shouldn't be included outside tuner-xc2028 + * + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License v2 */ @@ -54,11 +57,13 @@ /* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M) and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) There are variants both with and without NOGD + Those firmwares produce better result with LCD displays */ #define LCD (1<<12) /* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M) and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) + The NOGD firmwares don't have group delay compensation filter */ #define NOGD (1<<13) @@ -85,11 +90,31 @@ /* This flag identifies that the scode table has a new format */ #define HAS_IF (1 << 30) -#define SCODE_TYPES (MTS|DTV6|QAM|DTV7|DTV78|DTV8|LCD|NOGD|MONO|ATSC|IF| \ +#if 0 +/* The current seek algorithm doesn'use those flags. Instead, this is + currently useful only for debug process, since this is a HINT table + associating an alias to an IF table that can be used by that digital + video standard (in the case of DTV6/7/78/8, QAM and ATSC), or to a + particular demod type + */ +#define SCODE_TYPES (DTV6|QAM|DTV7|DTV78|DTV8|LCD|NOGD|MONO|ATSC|IF| \ LG60|ATI638|OREN538|OREN36|TOYOTA388|TOYOTA794| \ DIBCOM52|ZARLINK456|CHINA|F6MHZ|SCODE) - -/* Newer types to be moved to videodev2.h */ +#else +/* There are different scode tables for MTS and non-MTS. + The MTS firmwares support mono only + */ +#define SCODE_TYPES (SCODE | MTS) +#endif + + +/* Newer types not defined on videodev2.h. + The original idea were to move all those types to videodev2.h, but + it seemed overkill, since, with the exception of SECAM/K3, the other + types seem to be autodetected. + It is not clear where secam/k3 is used, nor we have a feedback of this + working or being autodetected by the standard secam firmware. + */ #define V4L2_STD_SECAM_K3 (0x04000000) diff --git a/linux/drivers/media/video/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index 1d1a9854b..6a9742552 100644 --- a/linux/drivers/media/video/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -1,6 +1,6 @@ /* tuner-xc2028 * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) * - frontend interface @@ -144,7 +144,7 @@ struct xc2028_data { _rc; \ }) -static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) +static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) { unsigned char buf[2]; unsigned char ibuf[2]; @@ -249,6 +249,7 @@ static v4l2_std_id parse_audio_std_option(void) static void free_firmware(struct xc2028_data *priv) { int i; + tuner_dbg("%s called\n", __func__); if (!priv->firm) return; @@ -417,7 +418,7 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, { struct xc2028_data *priv = fe->tuner_priv; int i, best_i = -1, best_nr_matches = 0; - unsigned int ign_firm_type_mask = 0; + unsigned int type_mask = 0; tuner_dbg("%s called, want type=", __func__); if (debug) { @@ -434,18 +435,23 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, *id = V4L2_STD_PAL; if (type & BASE) - type &= BASE_TYPES; + type_mask = BASE_TYPES; else if (type & SCODE) { type &= SCODE_TYPES; - ign_firm_type_mask = HAS_IF; + type_mask = SCODE_TYPES & ~HAS_IF; } else if (type & DTV_TYPES) - type &= DTV_TYPES; + type_mask = DTV_TYPES; else if (type & STD_SPECIFIC_TYPES) - type &= STD_SPECIFIC_TYPES; + type_mask = STD_SPECIFIC_TYPES; + + type &= type_mask; + + if (!(type & SCODE)) + type_mask = ~0; /* Seek for exact match */ for (i = 0; i < priv->firm_size; i++) { - if ((type == (priv->firm[i].type & ~ign_firm_type_mask)) && + if ((type == (priv->firm[i].type & type_mask)) && (*id == priv->firm[i].id)) goto found; } @@ -455,7 +461,7 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, v4l2_std_id match_mask; int nr_matches; - if (type != (priv->firm[i].type & ~ign_firm_type_mask)) + if (type != (priv->firm[i].type & type_mask)) continue; match_mask = *id & priv->firm[i].id; @@ -792,10 +798,10 @@ check_device: goto fail; } - tuner_info("Device is Xceive %d version %d.%d, " - "firmware version %d.%d\n", - hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, - (version & 0xf0) >> 4, version & 0xf); + tuner_dbg("Device is Xceive %d version %d.%d, " + "firmware version %d.%d\n", + hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, + (version & 0xf0) >> 4, version & 0xf); /* Check firmware version against what we downloaded. */ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { @@ -852,21 +858,28 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) /* Sync Lock Indicator */ rc = xc2028_get_reg(priv, 0x0002, &frq_lock); - if (rc < 0 || frq_lock == 0) + if (rc < 0) goto ret; - /* Frequency is locked. Return signal quality */ + /* Frequency is locked */ + if (frq_lock == 1) + signal = 32768; /* Get SNR of the video signal */ rc = xc2028_get_reg(priv, 0x0040, &signal); if (rc < 0) - signal = -frq_lock; + goto ret; + + /* Use both frq_lock and signal to generate the result */ + signal = signal || ((signal & 0x07) << 12); ret: mutex_unlock(&priv->lock); *strength = signal; + tuner_dbg("signal strength is %d\n", signal); + return rc; } @@ -1064,6 +1077,10 @@ static int xc2028_set_params(struct dvb_frontend *fe, T_DIGITAL_TV, type, 0, demod); } +#if 0 +/* This is needed at sleep (S1/S3), but not at fe_standby. Otherwise, + firmware will be loaded on every open() + */ static int xc2028_sleep(struct dvb_frontend *fe) { struct xc2028_data *priv = fe->tuner_priv; @@ -1084,7 +1101,7 @@ static int xc2028_sleep(struct dvb_frontend *fe) return rc; } - +#endif static int xc2028_dvb_release(struct dvb_frontend *fe) { @@ -1131,21 +1148,21 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) mutex_lock(&priv->lock); - kfree(priv->ctrl.fname); - free_firmware(priv); - memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - priv->ctrl.fname = NULL; + if (priv->ctrl.max_len < 9) + priv->ctrl.max_len = 13; if (p->fname) { + if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) { + kfree(priv->ctrl.fname); + free_firmware(priv); + } + priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); if (priv->ctrl.fname == NULL) rc = -ENOMEM; } - if (priv->ctrl.max_len < 9) - priv->ctrl.max_len = 13; - mutex_unlock(&priv->lock); return rc; @@ -1165,8 +1182,9 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .get_frequency = xc2028_get_frequency, .get_rf_strength = xc2028_signal, .set_params = xc2028_set_params, +#if 0 .sleep = xc2028_sleep, - +#endif #if 0 int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); int (*get_status)(struct dvb_frontend *fe, u32 *status); diff --git a/linux/drivers/media/video/tuner-xc2028.h b/linux/drivers/media/common/tuners/tuner-xc2028.h index 612e49063..216025cf5 100644 --- a/linux/drivers/media/video/tuner-xc2028.h +++ b/linux/drivers/media/common/tuners/tuner-xc2028.h @@ -1,6 +1,6 @@ /* tuner-xc2028 * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License v2 */ @@ -12,7 +12,7 @@ #define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw" /* Dmoduler IF (kHz) */ -#define XC3028_FE_DEFAULT 0 +#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */ #define XC3028_FE_LG60 6000 #define XC3028_FE_ATI638 6380 #define XC3028_FE_OREN538 5380 @@ -47,7 +47,7 @@ struct xc2028_config { #define XC2028_TUNER_RESET 0 #define XC2028_RESET_CLK 1 -#if defined(CONFIG_TUNER_XC2028) || (defined(CONFIG_TUNER_XC2028_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE)) extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg); #else diff --git a/linux/drivers/media/dvb/frontends/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index c832f39ab..b35640824 100644 --- a/linux/drivers/media/dvb/frontends/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -212,7 +212,7 @@ static void xc5000_TunerReset(struct dvb_frontend *fe) dprintk(1, "%s()\n", __func__); if (priv->cfg->tuner_callback) { - ret = priv->cfg->tuner_callback(priv->cfg->priv, + ret = priv->cfg->tuner_callback(priv->devptr, XC5000_TUNER_RESET, 0); if (ret) printk(KERN_ERR "xc5000: reset failed\n"); @@ -919,9 +919,9 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = { .get_status = xc5000_get_status }; -struct dvb_frontend * xc5000_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct xc5000_config *cfg) +struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct xc5000_config *cfg, void *devptr) { struct xc5000_priv *priv = NULL; u16 id = 0; @@ -935,6 +935,7 @@ struct dvb_frontend * xc5000_attach(struct dvb_frontend *fe, priv->cfg = cfg; priv->bandwidth = BANDWIDTH_6_MHZ; priv->i2c = i2c; + priv->devptr = devptr; /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. diff --git a/linux/drivers/media/dvb/frontends/xc5000.h b/linux/drivers/media/common/tuners/xc5000.h index b890883a0..c910715ad 100644 --- a/linux/drivers/media/dvb/frontends/xc5000.h +++ b/linux/drivers/media/common/tuners/xc5000.h @@ -31,33 +31,35 @@ struct xc5000_config { u8 i2c_address; u32 if_khz; - /* For each bridge framework, when it attaches either analog or digital, - * it has to store a reference back to its _core equivalent structure, - * so that it can service the hardware by steering gpio's etc. - * Each bridge implementation is different so cast priv accordingly. - * The xc5000 driver cares not for this value, other than ensuring - * it's passed back to a bridge during tuner_callback(). - */ - void *priv; int (*tuner_callback) (void *priv, int command, int arg); }; /* xc5000 callback command */ #define XC5000_TUNER_RESET 0 -#if defined(CONFIG_DVB_TUNER_XC5000) || \ - (defined(CONFIG_DVB_TUNER_XC5000_MODULE) && defined(MODULE)) +/* For each bridge framework, when it attaches either analog or digital, + * it has to store a reference back to its _core equivalent structure, + * so that it can service the hardware by steering gpio's etc. + * Each bridge implementation is different so cast devptr accordingly. + * The xc5000 driver cares not for this value, other than ensuring + * it's passed back to a bridge during tuner_callback(). + */ + +#if defined(CONFIG_MEDIA_TUNER_XC5000) || \ + (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE)) extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg); + struct xc5000_config *cfg, + void *devptr); #else static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg) + struct xc5000_config *cfg, + void *devptr) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif // CONFIG_DVB_TUNER_XC5000 +#endif // CONFIG_MEDIA_TUNER_XC5000 #endif // __XC5000_H__ diff --git a/linux/drivers/media/dvb/frontends/xc5000_priv.h b/linux/drivers/media/common/tuners/xc5000_priv.h index 13b2d1934..ecebfe474 100644 --- a/linux/drivers/media/dvb/frontends/xc5000_priv.h +++ b/linux/drivers/media/common/tuners/xc5000_priv.h @@ -31,6 +31,8 @@ struct xc5000_priv { u8 video_standard; u8 rf_mode; u8 fwloaded; + + void *devptr; }; #endif diff --git a/linux/drivers/media/dvb/Kconfig b/linux/drivers/media/dvb/Kconfig index 03ef88acd..7b21b49f1 100644 --- a/linux/drivers/media/dvb/Kconfig +++ b/linux/drivers/media/dvb/Kconfig @@ -1,9 +1,7 @@ # -# Multimedia device configuration +# DVB device configuration # -source "drivers/media/dvb/dvb-core/Kconfig" - menuconfig DVB_CAPTURE_DRIVERS bool "DVB/ATSC adapters" depends on DVB_CORE diff --git a/linux/drivers/media/dvb/b2c2/Kconfig b/linux/drivers/media/dvb/b2c2/Kconfig index 6ec5afba1..73dc2ee9b 100644 --- a/linux/drivers/media/dvb/b2c2/Kconfig +++ b/linux/drivers/media/dvb/b2c2/Kconfig @@ -9,7 +9,7 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select DVB_S5H1420 if !DVB_FE_CUSTOMISE select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE diff --git a/linux/drivers/media/dvb/b2c2/Makefile b/linux/drivers/media/dvb/b2c2/Makefile index 870e2848c..d9db066f9 100644 --- a/linux/drivers/media/dvb/b2c2/Makefile +++ b/linux/drivers/media/dvb/b2c2/Makefile @@ -14,4 +14,4 @@ b2c2-flexcop-usb-objs = flexcop-usb.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ -EXTRA_CFLAGS += -Idrivers/media/video/ +EXTRA_CFLAGS += -Idrivers/media/common/tuners/ diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 04989b7a1..f9d087669 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -252,7 +252,7 @@ static struct stv0299_config samsung_tbmu24112_config = { .mclk = 88000000UL, .invert = 0, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_LK, + .lock_output = STV0299_LOCKOUTPUT_LK, .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, @@ -634,7 +634,7 @@ int flexcop_frontend_init(struct flexcop_device *fc) } /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */ - fc->fe = dvb_attach(vp310_mt312_attach, + fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c); if (fc->fe != NULL) { ops = &fc->fe->ops; diff --git a/linux/drivers/media/dvb/bt8xx/Kconfig b/linux/drivers/media/dvb/bt8xx/Kconfig index 902c762e0..7588db131 100644 --- a/linux/drivers/media/dvb/bt8xx/Kconfig +++ b/linux/drivers/media/dvb/bt8xx/Kconfig @@ -1,6 +1,7 @@ config DVB_BT8XX tristate "BT8xx based PCI cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 + depends on HOTPLUG # due to FW_LOADER select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_SP887X if !DVB_FE_CUSTOMISE select DVB_NXT6000 if !DVB_FE_CUSTOMISE @@ -8,7 +9,7 @@ config DVB_BT8XX select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select FW_LOADER help Support for PCI cards based on the Bt8xx PCI bridge. Examples are diff --git a/linux/drivers/media/dvb/bt8xx/Makefile b/linux/drivers/media/dvb/bt8xx/Makefile index 9d3e68b5d..d98f1d49f 100644 --- a/linux/drivers/media/dvb/bt8xx/Makefile +++ b/linux/drivers/media/dvb/bt8xx/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/video/bt8xx -EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/dvb/bt8xx/dst.c b/linux/drivers/media/dvb/bt8xx/dst.c index 75711bde2..a7637562e 100644 --- a/linux/drivers/media/dvb/bt8xx/dst.c +++ b/linux/drivers/media/dvb/bt8xx/dst.c @@ -1714,7 +1714,7 @@ static void dst_release(struct dvb_frontend *fe) struct dst_state *state = fe->demodulator_priv; if (state->dst_ca) { dvb_unregister_device(state->dst_ca); -#ifdef CONFIG_DVB_CORE_ATTACH +#ifdef CONFIG_MEDIA_ATTACH symbol_put(dst_ca_attach); #endif } diff --git a/linux/drivers/media/dvb/cinergyT2/Kconfig b/linux/drivers/media/dvb/cinergyT2/Kconfig index 3d778c5ab..c03513b2c 100644 --- a/linux/drivers/media/dvb/cinergyT2/Kconfig +++ b/linux/drivers/media/dvb/cinergyT2/Kconfig @@ -1,6 +1,6 @@ config DVB_CINERGYT2 tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver" - depends on DVB_CORE && USB + depends on DVB_CORE && USB && INPUT help Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers diff --git a/linux/drivers/media/dvb/dvb-core/Kconfig b/linux/drivers/media/dvb/dvb-core/Kconfig deleted file mode 100644 index e3e6839f8..000000000 --- a/linux/drivers/media/dvb/dvb-core/Kconfig +++ /dev/null @@ -1,34 +0,0 @@ -config DVB_CORE - tristate "DVB for Linux" - depends on NET && INET - select CRC32 - help - Support Digital Video Broadcasting hardware. Enable this if you - own a DVB adapter and want to use it or if you compile Linux for - a digital SetTopBox. - - DVB core utility functions for device handling, software fallbacks etc. - Say Y when you have a DVB card and want to use it. Say Y if your want - to build your drivers outside the kernel, but need the DVB core. All - in-kernel drivers will select this automatically if needed. - - API specs and user tools are available from <http://www.linuxtv.org/>. - - Please report problems regarding this driver to the LinuxDVB - mailing list. - - If unsure say N. - -config DVB_CORE_ATTACH - bool "Load and attach frontend modules as needed" - depends on DVB_CORE - depends on MODULES - help - Remove the static dependency of DVB card drivers on all - frontend modules for all possible card variants. Instead, - allow the card drivers to only load the frontend modules - they require. This saves several KBytes of memory. - - Note: You will need module-init-tools v3.2 or later for this feature. - - If unsure say Y. diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index 3415a3bb3..df5bef6a2 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -259,6 +259,39 @@ static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, return ret; } +static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev, + unsigned long size) +{ + struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer; + void *newmem; + void *oldmem; + + dprintk("function : %s\n", __func__); + + if (buf->size == size) + return 0; + if (!size) + return -EINVAL; + + newmem = vmalloc(size); + if (!newmem) + return -ENOMEM; + + oldmem = buf->data; + + spin_lock_irq(&dmxdev->lock); + buf->data = newmem; + buf->size = size; + + /* reset and not flush in case the buffer shrinks */ + dvb_ringbuffer_reset(buf); + spin_unlock_irq(&dmxdev->lock); + + vfree(oldmem); + + return 0; +} + static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter *dmxdevfilter, int state) { @@ -271,28 +304,32 @@ static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, unsigned long size) { struct dvb_ringbuffer *buf = &dmxdevfilter->buffer; - void *mem; + void *newmem; + void *oldmem; if (buf->size == size) return 0; + if (!size) + return -EINVAL; if (dmxdevfilter->state >= DMXDEV_STATE_GO) return -EBUSY; + + newmem = vmalloc(size); + if (!newmem) + return -ENOMEM; + + oldmem = buf->data; + spin_lock_irq(&dmxdevfilter->dev->lock); - mem = buf->data; - buf->data = NULL; + buf->data = newmem; buf->size = size; - dvb_ringbuffer_flush(buf); + + /* reset and not flush in case the buffer shrinks */ + dvb_ringbuffer_reset(buf); spin_unlock_irq(&dmxdevfilter->dev->lock); - vfree(mem); - if (buf->size) { - mem = vmalloc(dmxdevfilter->buffer.size); - if (!mem) - return -ENOMEM; - spin_lock_irq(&dmxdevfilter->dev->lock); - buf->data = mem; - spin_unlock_irq(&dmxdevfilter->dev->lock); - } + vfree(oldmem); + return 0; } @@ -1009,6 +1046,7 @@ static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file, { struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; + unsigned long arg = (unsigned long)parg; int ret; if (mutex_lock_interruptible(&dmxdev->mutex)) @@ -1016,8 +1054,7 @@ static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file, switch (cmd) { case DMX_SET_BUFFER_SIZE: - // FIXME: implement - ret = 0; + ret = dvb_dvr_set_buffer_size(dmxdev, arg); break; default: diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index 8e197b831..02d902fdf 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -911,15 +911,21 @@ static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca) int curdelay = 100000000; int slot; + /* Beware of too high polling frequency, because one polling + * call might take several hundred milliseconds until timeout! + */ for (slot = 0; slot < ca->slot_count; slot++) { switch (ca->slot_info[slot].slot_state) { default: case DVB_CA_SLOTSTATE_NONE: + delay = HZ * 60; /* 60s */ + if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) + delay = HZ * 5; /* 5s */ + break; case DVB_CA_SLOTSTATE_INVALID: - delay = HZ * 60; - if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) { - delay = HZ / 10; - } + delay = HZ * 60; /* 60s */ + if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) + delay = HZ / 10; /* 100ms */ break; case DVB_CA_SLOTSTATE_UNINITIALISED: @@ -927,19 +933,17 @@ static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca) case DVB_CA_SLOTSTATE_VALIDATE: case DVB_CA_SLOTSTATE_WAITFR: case DVB_CA_SLOTSTATE_LINKINIT: - delay = HZ / 10; + delay = HZ / 10; /* 100ms */ break; case DVB_CA_SLOTSTATE_RUNNING: - delay = HZ * 60; - if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) { - delay = HZ / 10; - } + delay = HZ * 60; /* 60s */ + if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) + delay = HZ / 10; /* 100ms */ if (ca->open) { if ((!ca->slot_info[slot].da_irq_supported) || - (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) { - delay = HZ / 10; - } + (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) + delay = HZ / 10; /* 100ms */ } break; } diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index c1855f562..8a9c2283f 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1200,7 +1200,7 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) } EXPORT_SYMBOL(dvb_unregister_frontend); -#ifdef CONFIG_DVB_CORE_ATTACH +#ifdef CONFIG_MEDIA_ATTACH void dvb_frontend_detach(struct dvb_frontend* fe) { void *ptr; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c index ac9d93cf8..872985b79 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c @@ -90,7 +90,11 @@ void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) rbuf->error = 0; } - +void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) +{ + rbuf->pread = rbuf->pwrite = 0; + rbuf->error = 0; +} void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) { diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h index d97714e75..890826262 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h @@ -69,6 +69,7 @@ struct dvb_ringbuffer { ** to lock read or write operations. ** Two or more readers must be locked against each other. ** Flushing the buffer counts as a read operation. +** Resetting the buffer counts as a read and write operation. ** Two or more writers must be locked against each other. */ @@ -85,6 +86,13 @@ extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf); extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf); +/* +** Reset the read and write pointers to zero and flush the buffer +** This counts as a read and write operation +*/ +extern void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf); + + /* read routines & macros */ /* ---------------------- */ /* flush buffer */ diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.h b/linux/drivers/media/dvb/dvb-core/dvbdev.h index 5f9a737c6..89d12dc47 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.h +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.h @@ -115,7 +115,7 @@ extern int dvb_usercopy(struct inode *inode, struct file *file, unsigned int cmd, void *arg)); /** generic DVB attach function. */ -#ifdef CONFIG_DVB_CORE_ATTACH +#ifdef CONFIG_MEDIA_ATTACH #define dvb_attach(FUNCTION, ARGS...) ({ \ void *__r = NULL; \ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index 3c8493d20..615e5bbec 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -1,6 +1,7 @@ config DVB_USB tristate "Support for various USB DVB devices" - depends on DVB_CORE && USB && I2C + depends on DVB_CORE && USB && I2C && INPUT + depends on HOTPLUG # due to FW_LOADER select FW_LOADER help By enabling this you will be able to choose the various supported @@ -25,7 +26,7 @@ config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB select DVB_DIB3000MC - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. @@ -35,7 +36,7 @@ config DVB_USB_DIBUSB_MB depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MB - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator. @@ -56,7 +57,7 @@ config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_DIB3000MC - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE help Support for USB2.0 DVB-T receivers based on reference designs made by DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator. @@ -73,8 +74,8 @@ config DVB_USB_DIB0700 select DVB_DIB7000P select DVB_DIB7000M select DVB_DIB3000MC - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select DVB_TUNER_MT2266 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE select DVB_TUNER_DIB0070 help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The @@ -93,7 +94,7 @@ config DVB_USB_UMT_010 depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MC - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE help Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. @@ -105,7 +106,7 @@ config DVB_USB_CXUSB select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -118,7 +119,7 @@ config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB select DVB_MT352 if !DVB_FE_CUSTOMISE - select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE help Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Currently, only devices with a product id of @@ -129,7 +130,7 @@ config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 receiver with USB ID 0db0:5581. @@ -138,7 +139,7 @@ config DVB_USB_AU6610 tristate "Alcor Micro AU6610 USB2.0 support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE help Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. @@ -190,7 +191,7 @@ config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. @@ -227,8 +228,8 @@ config DVB_USB_OPERA1 config DVB_USB_AF9005 tristate "Afatech AF9005 DVB-T USB1.1 support" depends on DVB_USB && EXPERIMENTAL - select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE help Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver and the TerraTec Cinergy T USB XE (Rev.1) @@ -240,3 +241,13 @@ config DVB_USB_AF9005_REMOTE Say Y here to support the default remote control decoding for the Afatech AF9005 based receiver. +config DVB_USB_ANYSEE + tristate "Anysee DVB-T/C USB2.0 support" + depends on DVB_USB + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Anysee E30, Anysee E30 Plus or + Anysee E30 C Plus DVB USB2.0 receiver. + diff --git a/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile index 60a910052..44c11e45e 100644 --- a/linux/drivers/media/dvb/dvb-usb/Makefile +++ b/linux/drivers/media/dvb/dvb-usb/Makefile @@ -61,7 +61,10 @@ obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o dvb-usb-af9005-remote-objs = af9005-remote.o obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o +dvb-usb-anysee-objs = anysee.o +obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 -EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/dvb/dvb-usb/anysee.c b/linux/drivers/media/dvb/dvb-usb/anysee.c new file mode 100644 index 000000000..89675dbf7 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/anysee.c @@ -0,0 +1,558 @@ +/* + * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * 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. + * + * TODO: + * - add smart card reader support for Conditional Access (CA) + * + * Card reader in Anysee is nothing more than ISO 7816 card reader. + * There is no hardware CAM in any Anysee device sold. + * In my understanding it should be implemented by making own module + * for ISO 7816 card reader, like dvb_ca_en50221 is implented. This + * module registers serial interface that can be used to comminicate + * with any ISO 7816 smart card. + * + * Any help according to implement serial smart card reader support + * is highly welcome! + */ + +#include "anysee.h" +#include "tda1002x.h" +#include "mt352.h" +#include "mt352_priv.h" +#include "zl10353.h" + +/* debug */ +static int dvb_usb_anysee_debug; +module_param_named(debug, dvb_usb_anysee_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct mutex anysee_usb_mutex; + +static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, + u8 *rbuf, u8 rlen) +{ + struct anysee_state *state = d->priv; + int act_len, ret; + u8 buf[64]; + + if (slen > sizeof(buf)) + slen = sizeof(buf); + memcpy(&buf[0], sbuf, slen); + buf[60] = state->seq++; + + if (mutex_lock_interruptible(&anysee_usb_mutex) < 0) + return -EAGAIN; + + /* We need receive one message more after dvb_usb_generic_rw due + to weird transaction flow, which is 1 x send + 2 x receive. */ + ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0); + + if (!ret) { + /* receive 2nd answer */ + ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf), + &act_len, 2000); + if (ret) + err("%s: recv bulk message failed: %d", __func__, ret); + else { + deb_xfer("<<< "); + debug_dump(buf, act_len, deb_xfer); + } + } + + /* read request, copy returned data to return buf */ + if (!ret && rbuf && rlen) + memcpy(rbuf, buf, rlen); + + mutex_unlock(&anysee_usb_mutex); + + return ret; +} + +static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +{ + u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01}; + int ret; + ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1); + deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val); + return ret; +} + +static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val) +{ + u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val}; + deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id) +{ + u8 buf[] = {CMD_GET_HW_INFO}; + return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3); +} + +static int anysee_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00}; + deb_info("%s: onoff:%02x\n", __func__, onoff); + return anysee_ctrl_msg(adap->dev, buf, sizeof(buf), NULL, 0); +} + +static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval) +{ + u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval}; + deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff) +{ + u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff}; + deb_info("%s: onoff:%02x\n", __func__, onoff); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_init(struct dvb_usb_device *d) +{ + int ret; + /* LED light */ + ret = anysee_led_ctrl(d, 0x01, 0x03); + if (ret) + return ret; + + /* enable IR */ + ret = anysee_ir_ctrl(d, 1); + if (ret) + return ret; + + return 0; +} + +/* I2C */ +static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret, inc, i = 0; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + u8 buf[6]; + buf[0] = CMD_I2C_READ; + buf[1] = msg[i].addr + 1; + buf[2] = msg[i].buf[0]; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x01; + ret = anysee_ctrl_msg(d, buf, sizeof(buf), msg[i+1].buf, + msg[i+1].len); + inc = 2; + } else { + u8 buf[4+msg[i].len]; + buf[0] = CMD_I2C_WRITE; + buf[1] = msg[i].addr; + buf[2] = msg[i].len; + buf[3] = 0x01; + memcpy(&buf[4], msg[i].buf, msg[i].len); + ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); + inc = 1; + } + if (ret) + return ret; + + i += inc; + } + + mutex_unlock(&d->i2c_mutex); + + return i; +} + +static u32 anysee_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm anysee_i2c_algo = { + .master_xfer = anysee_master_xfer, + .functionality = anysee_i2c_func, +}; + +static int anysee_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + +/* Callbacks for DVB USB */ +static struct tda10023_config anysee_tda10023_config = { + .demod_address = 0x1a, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .deltaf = 0xfed6, +}; + +static struct mt352_config anysee_mt352_config = { + .demod_address = 0x1e, + .demod_init = anysee_mt352_demod_init, +}; + +static struct zl10353_config anysee_zl10353_config = { + .demod_address = 0x1e, + .parallel_ts = 1, +}; + +static int anysee_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct anysee_state *state = adap->dev->priv; + u8 hw_info[3]; + u8 io_d; /* IO port D */ + + /* check which hardware we have + We must do this call two times to get reliable values (hw bug). */ + ret = anysee_get_hw_info(adap->dev, hw_info); + if (ret) + return ret; + ret = anysee_get_hw_info(adap->dev, hw_info); + if (ret) + return ret; + + /* Meaning of these info bytes are guessed. */ + info("firmware version:%d.%d.%d hardware id:%d", + 0, hw_info[1], hw_info[2], hw_info[0]); + + ret = anysee_read_reg(adap->dev, 0xb0, &io_d); /* IO port D */ + if (ret) + return ret; + deb_info("%s: IO port D:%02x\n", __func__, io_d); + + /* Select demod using trial and error method. */ + + /* Try to attach demodulator in following order: + model demod hw firmware + 1. E30 MT352 02 0.2.1 + 2. E30 ZL10353 02 0.2.1 + 3. E30 Plus ZL10353 06 0.1.0 + 4. E30C Plus TDA10023 0a 0.1.0 + E30C Plus TDA10023 0f 0.1.2 (not working) + */ + + /* Zarlink MT352 DVB-T demod inside of Samsung DNOS404ZH102A NIM */ + adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_THOMSON_DTT7579; + return 0; + } + + /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */ + adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_THOMSON_DTT7579; + return 0; + } + + /* connect demod on IO port D for TDA10023 & ZL10353 */ + ret = anysee_write_reg(adap->dev, 0xb0, 0x25); + if (ret) + return ret; + + /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */ + adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_THOMSON_DTT7579; + return 0; + } + + /* known not working (E30C Plus v0.1.2) */ + if (hw_info[0] == 0x0f) { + info("this version of Anysee is not supported yet"); + /* return IO port D to init value for safe */ + ret = anysee_write_reg(adap->dev, 0xb0, io_d); + return -ENODEV; + } + + /* Philips TDA10023 DVB-C demod */ + adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config, + &adap->dev->i2c_adap, 0x48); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_SAMSUNG_DTOS403IH102A; + return 0; + } + + /* return IO port D to init value for safe */ + ret = anysee_write_reg(adap->dev, 0xb0, io_d); + if (ret) + return ret; + + err("Unkown Anysee version: %02x %02x %02x. "\ + "Please report the <linux-dvb@linuxtv.org>.", + hw_info[0], hw_info[1], hw_info[2]); + + return -ENODEV; +} + +static int anysee_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct anysee_state *state = adap->dev->priv; + deb_info("%s: \n", __func__); + + switch (state->tuner) { + case DVB_PLL_THOMSON_DTT7579: + /* Thomson dtt7579 (not sure) PLL inside of: + Samsung DNOS404ZH102A NIM + Samsung DNOS404ZH103A NIM */ + dvb_attach(dvb_pll_attach, adap->fe, 0x61, + NULL, DVB_PLL_THOMSON_DTT7579); + break; + case DVB_PLL_SAMSUNG_DTOS403IH102A: + /* Unknown PLL inside of Samsung DTOS403IH102A tuner module */ + dvb_attach(dvb_pll_attach, adap->fe, 0xc0, + &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + break; + } + + return 0; +} + +static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 buf[] = {CMD_GET_IR_CODE}; + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + u8 ircode[2]; + int i, ret; + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), &ircode[0], 2); + if (ret) + return ret; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (keymap[i].custom == ircode[0] && + keymap[i].data == ircode[1]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + return 0; + } + } + return 0; +} + +static struct dvb_usb_rc_key anysee_rc_keys[] = { + { 0x01, 0x00, KEY_0 }, + { 0x01, 0x01, KEY_1 }, + { 0x01, 0x02, KEY_2 }, + { 0x01, 0x03, KEY_3 }, + { 0x01, 0x04, KEY_4 }, + { 0x01, 0x05, KEY_5 }, + { 0x01, 0x06, KEY_6 }, + { 0x01, 0x07, KEY_7 }, + { 0x01, 0x08, KEY_8 }, + { 0x01, 0x09, KEY_9 }, + { 0x01, 0x0a, KEY_POWER }, + { 0x01, 0x0b, KEY_DOCUMENTS }, /* * */ + { 0x01, 0x19, KEY_FAVORITES }, + { 0x01, 0x20, KEY_SLEEP }, + { 0x01, 0x21, KEY_MODE }, /* 4:3 / 16:9 select */ + { 0x01, 0x22, KEY_ZOOM }, + { 0x01, 0x47, KEY_TEXT }, + { 0x01, 0x16, KEY_TV }, /* TV / radio select */ + { 0x01, 0x1e, KEY_LANGUAGE }, /* Second Audio Program */ + { 0x01, 0x1a, KEY_SUBTITLE }, + { 0x01, 0x1b, KEY_CAMERA }, /* screenshot */ + { 0x01, 0x42, KEY_MUTE }, + { 0x01, 0x0e, KEY_MENU }, + { 0x01, 0x0f, KEY_EPG }, + { 0x01, 0x17, KEY_INFO }, + { 0x01, 0x10, KEY_EXIT }, + { 0x01, 0x13, KEY_VOLUMEUP }, + { 0x01, 0x12, KEY_VOLUMEDOWN }, + { 0x01, 0x11, KEY_CHANNELUP }, + { 0x01, 0x14, KEY_CHANNELDOWN }, + { 0x01, 0x15, KEY_OK }, + { 0x01, 0x1d, KEY_RED }, + { 0x01, 0x1f, KEY_GREEN }, + { 0x01, 0x1c, KEY_YELLOW }, + { 0x01, 0x44, KEY_BLUE }, + { 0x01, 0x0c, KEY_SHUFFLE }, /* snapshot */ + { 0x01, 0x48, KEY_STOP }, + { 0x01, 0x50, KEY_PLAY }, + { 0x01, 0x51, KEY_PAUSE }, + { 0x01, 0x49, KEY_RECORD }, + { 0x01, 0x18, KEY_PREVIOUS }, /* |<< */ + { 0x01, 0x0d, KEY_NEXT }, /* >>| */ + { 0x01, 0x24, KEY_PROG1 }, /* F1 */ + { 0x01, 0x25, KEY_PROG2 }, /* F2 */ +}; + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties anysee_properties; + +static int anysee_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + mutex_init(&anysee_usb_mutex); + + /* There is one interface with two alternate settings. + Alternate setting 0 is for bulk transfer. + Alternate setting 1 is for isochronous transfer. + We use bulk transfer (alternate setting 0). */ + if (intf->num_altsetting < 1) + return -ENODEV; + + ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d, + adapter_nr); + if (ret) + return ret; + + alt = usb_altnum_to_altsetting(intf, 0); + if (alt == NULL) { + deb_info("%s: no alt found!\n", __func__); + return -ENODEV; + } + + ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret) + return ret; + + if (d) + ret = anysee_init(d); + + return ret; +} + +static struct usb_device_id anysee_table [] = { + { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) }, + { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, anysee_table); + +static struct dvb_usb_device_properties anysee_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = sizeof(struct anysee_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = anysee_streaming_ctrl, + .frontend_attach = anysee_frontend_attach, + .tuner_attach = anysee_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + } + }, + + .rc_key_map = anysee_rc_keys, + .rc_key_map_size = ARRAY_SIZE(anysee_rc_keys), + .rc_query = anysee_rc_query, + .rc_interval = 200, /* windows driver uses 500ms */ + + .i2c_algo = &anysee_i2c_algo, + + .generic_bulk_ctrl_endpoint = 1, + + .num_device_descs = 1, + .devices = { + { + .name = "Anysee DVB USB2.0", + .cold_ids = {NULL}, + .warm_ids = {&anysee_table[0], + &anysee_table[1], NULL}, + }, + } +}; + +static struct usb_driver anysee_driver = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) + .owner = THIS_MODULE, +#endif + .name = "dvb_usb_anysee", + .probe = anysee_probe, + .disconnect = dvb_usb_device_exit, + .id_table = anysee_table, +}; + +/* module stuff */ +static int __init anysee_module_init(void) +{ + int ret; + + ret = usb_register(&anysee_driver); + if (ret) + err("%s: usb_register failed. Error number %d", __func__, ret); + + return ret; +} + +static void __exit anysee_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&anysee_driver); +} + +module_init(anysee_module_init); +module_exit(anysee_module_exit); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/anysee.h b/linux/drivers/media/dvb/dvb-usb/anysee.h new file mode 100644 index 000000000..48da3949e --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/anysee.h @@ -0,0 +1,304 @@ +/* + * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * 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. + * + * TODO: + * - add smart card reader support for Conditional Access (CA) + * + * Card reader in Anysee is nothing more than ISO 7816 card reader. + * There is no hardware CAM in any Anysee device sold. + * In my understanding it should be implemented by making own module + * for ISO 7816 card reader, like dvb_ca_en50221 is implented. This + * module registers serial interface that can be used to comminicate + * with any ISO 7816 smart card. + * + * Any help according to implement serial smart card reader support + * is highly welcome! + */ + +#ifndef _DVB_USB_ANYSEE_H_ +#define _DVB_USB_ANYSEE_H_ + +#define DVB_USB_LOG_PREFIX "anysee" +#include "dvb-usb.h" + +#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args) + +enum cmd { + CMD_I2C_READ = 0x33, + CMD_I2C_WRITE = 0x31, + CMD_REG_READ = 0xb0, + CMD_REG_WRITE = 0xb1, + CMD_STREAMING_CTRL = 0x12, + CMD_LED_AND_IR_CTRL = 0x16, + CMD_GET_IR_CODE = 0x41, + CMD_GET_HW_INFO = 0x19, + CMD_SMARTCARD = 0x34, +}; + +struct anysee_state { + u8 tuner; + u8 seq; +}; + +#endif + +/*************************************************************************** + * USB API description (reverse engineered) + *************************************************************************** + +Transaction flow: +================= +BULK[00001] >>> REQUEST PACKET 64 bytes +BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY) +BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY) + +General reply packet(s) are always used if not own reply defined. + +============================================================================ +| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY) +============================================================================ +| 00 | reply data (if any) from previous transaction +| | Just same reply packet as returned during previous transaction. +| | Needed only if reply is missed in previous transaction. +| | Just skip normally. +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY) +============================================================================ +| 00 | reply data (if any) +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | I2C WRITE REQUEST PACKET +============================================================================ +| 00 | 0x31 I2C write command +---------------------------------------------------------------------------- +| 01 | i2c address +---------------------------------------------------------------------------- +| 02 | data length +| | 0x02 (for typical I2C reg / val pair) +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04- | data +---------------------------------------------------------------------------- +| -59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | I2C READ REQUEST PACKET +============================================================================ +| 00 | 0x33 I2C read command +---------------------------------------------------------------------------- +| 01 | i2c address + 1 +---------------------------------------------------------------------------- +| 02 | register +---------------------------------------------------------------------------- +| 03 | 0x00 +---------------------------------------------------------------------------- +| 04 | 0x00 +---------------------------------------------------------------------------- +| 05 | 0x01 +---------------------------------------------------------------------------- +| 06-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET +============================================================================ +| 00 | 0xb1 register write command +---------------------------------------------------------------------------- +| 01-02 | register +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04 | value +---------------------------------------------------------------------------- +| 05-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET +============================================================================ +| 00 | 0xb0 register read command +---------------------------------------------------------------------------- +| 01-02 | register +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | LED CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x16 LED and IR control command +---------------------------------------------------------------------------- +| 01 | 0x01 (LED) +---------------------------------------------------------------------------- +| 03 | 0x00 blink +| | 0x01 lights continuously +---------------------------------------------------------------------------- +| 04 | blink interval +| | 0x00 fastest (looks like LED lights continuously) +| | 0xff slowest +---------------------------------------------------------------------------- +| 05-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | IR CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x16 LED and IR control command +---------------------------------------------------------------------------- +| 01 | 0x02 (IR) +---------------------------------------------------------------------------- +| 03 | 0x00 IR disabled +| | 0x01 IR enabled +---------------------------------------------------------------------------- +| 04-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | STREAMING CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x12 streaming control command +---------------------------------------------------------------------------- +| 01 | 0x00 streaming disabled +| | 0x01 streaming enabled +---------------------------------------------------------------------------- +| 02 | 0x00 +---------------------------------------------------------------------------- +| 03-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | REMOTE CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x41 remote control command +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | REMOTE CONTROL REPLY PACKET +============================================================================ +| 00 | 0x00 code not received +| | 0x01 code received +---------------------------------------------------------------------------- +| 01 | remote control code +---------------------------------------------------------------------------- +| 02-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GET HARDWARE INFO REQUEST PACKET +============================================================================ +| 00 | 0x19 get hardware info command +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GET HARDWARE INFO REPLY PACKET +============================================================================ +| 00 | hardware id +---------------------------------------------------------------------------- +| 01-02 | firmware version +---------------------------------------------------------------------------- +| 03-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | SMART CARD READER PACKET +============================================================================ +| 00 | 0x34 smart card reader command +---------------------------------------------------------------------------- +| xx | +---------------------------------------------------------------------------- +| xx-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +*/ diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 8017f1e8a..ddfa0cff7 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -33,7 +33,6 @@ #include "mt352_priv.h" #include "zl10353.h" #include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" #include "tuner-simple.h" /* debug */ @@ -521,7 +520,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) static struct xc2028_ctrl ctl = { .fname = "xc3028-dvico-au-01.fw", .max_len = 64, - .scode_table = ZARLINK456, + .scode_table = XC3028_FE_ZARLINK456, }; fe = dvb_attach(xc2028_attach, adap->fe, &cfg); diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index 6477fc66c..346223856 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -299,7 +299,7 @@ static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) } /* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */ -struct dibx000_agc_config xc3028_agc_config = { +static struct dibx000_agc_config xc3028_agc_config = { BAND_VHF | BAND_UHF, /* band_caps */ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, @@ -342,7 +342,7 @@ struct dibx000_agc_config xc3028_agc_config = { }; /* PLL Configuration for COFDM BW_MHz = 8.00 with external clock = 30.00 */ -struct dibx000_bandwidth_config xc3028_bw_config = { +static struct dibx000_bandwidth_config xc3028_bw_config = { 60000, 30000, /* internal, sampling */ 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 34245d1b7..e6b43fb3a 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -14,6 +14,7 @@ #define USB_VID_AFATECH 0x15a4 #define USB_VID_ALCOR_MICRO 0x058f #define USB_VID_ALINK 0x05e3 +#define USB_VID_AMT 0x1c73 #define USB_VID_ANCHOR 0x0547 #define USB_VID_ANSONIC 0x10b9 #define USB_VID_ANUBIS_ELECTRONIC 0x10fd @@ -57,6 +58,7 @@ #define USB_PID_AFATECH_AF9005 0x9020 #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 +#define USB_PID_ANYSEE 0x861f #define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 #define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 diff --git a/linux/drivers/media/dvb/dvb-usb/gp8psk.c b/linux/drivers/media/dvb/dvb-usb/gp8psk.c index cc99e1cab..0a152fcb5 100644 --- a/linux/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/linux/drivers/media/dvb/dvb-usb/gp8psk.c @@ -146,24 +146,24 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) if (! (status & bm8pskFW_Loaded)) /* BCM4500 firmware loaded */ if(gp8psk_load_bcm4500fw(d)) - return EINVAL; + return -EINVAL; if (! (status & bmIntersilOn)) /* LNB Power */ if (gp8psk_usb_in_op(d, START_INTERSIL, 1, 0, &buf, 1)) - return EINVAL; + return -EINVAL; /* Set DVB mode to 1 */ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) if (gp8psk_usb_out_op(d, SET_DVB_MODE, 1, 0, NULL, 0)) - return EINVAL; + return -EINVAL; /* Abort possible TS (if previous tune crashed) */ if (gp8psk_usb_out_op(d, ARM_TRANSFER, 0, 0, NULL, 0)) - return EINVAL; + return -EINVAL; } else { /* Turn off LNB power */ if (gp8psk_usb_in_op(d, START_INTERSIL, 0, 0, &buf, 1)) - return EINVAL; + return -EINVAL; /* Turn off 8psk power */ if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) return -EINVAL; diff --git a/linux/drivers/media/dvb/dvb-usb/opera1.c b/linux/drivers/media/dvb/dvb-usb/opera1.c index 1cb2a5e90..cba064615 100644 --- a/linux/drivers/media/dvb/dvb-usb/opera1.c +++ b/linux/drivers/media/dvb/dvb-usb/opera1.c @@ -257,7 +257,7 @@ static struct stv0299_config opera1_stv0299_config = { .mclk = 88000000UL, .invert = 1, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_0, + .lock_output = STV0299_LOCKOUTPUT_0, .volt13_op0_op1 = STV0299_VOLT13_OP0, .inittab = opera1_inittab, .set_symbol_rate = opera1_stv0299_set_symbol_rate, diff --git a/linux/drivers/media/dvb/dvb-usb/ttusb2.c b/linux/drivers/media/dvb/dvb-usb/ttusb2.c index dd11a1fee..e79a26473 100644 --- a/linux/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/linux/drivers/media/dvb/dvb-usb/ttusb2.c @@ -153,6 +153,7 @@ static struct tda10086_config tda10086_config = { .demod_address = 0x0e, .invert = 0, .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, }; static int ttusb2_frontend_attach(struct dvb_usb_adapter *adap) diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 4284a3092..fa7f0c563 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -15,22 +15,36 @@ config DVB_FE_CUSTOMISE comment "DVB-S (satellite) frontends" depends on DVB_CORE -config DVB_STV0299 - tristate "ST STV0299 based" +config DVB_CX24110 + tristate "Conexant CX24110 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_CX24110 - tristate "Conexant CX24110 based" +config DVB_CX24123 + tristate "Conexant CX24123 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_CX24123 - tristate "Conexant CX24123 based" +config DVB_MT312 + tristate "Zarlink VP310/MT312/ZL10313 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_S5H1420 + tristate "Samsung S5H1420 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_STV0299 + tristate "ST STV0299 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help @@ -43,8 +57,8 @@ config DVB_TDA8083 help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_MT312 - tristate "Zarlink VP310/MT312 based" +config DVB_TDA10086 + tristate "Philips TDA10086 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help @@ -57,26 +71,33 @@ config DVB_VES1X93 help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_S5H1420 - tristate "Samsung S5H1420 based" +config DVB_TUNER_ITD1000 + tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help A DVB-S tuner module. Say Y when you want to support this frontend. -config DVB_TDA10086 - tristate "Philips TDA10086 based" +config DVB_TDA826X + tristate "Philips TDA826X silicon tuner" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help - A DVB-S tuner module. Say Y when you want to support this frontend. + A DVB-S silicon tuner module. Say Y when you want to support this tuner. + +config DVB_TUA6100 + tristate "Infineon TUA6100 PLL" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S PLL chip. comment "DVB-T (terrestrial) frontends" depends on DVB_CORE config DVB_SP8870 tristate "Spase sp8870 based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -89,7 +110,7 @@ config DVB_SP8870 config DVB_SP887X tristate "Spase sp887x based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -114,6 +135,20 @@ config DVB_CX22702 help A DVB-T tuner module. Say Y when you want to support this frontend. +config DVB_DRX397XD + tristate "Micronas DRX3975D/DRX3977D based" + depends on DVB_CORE && I2C && HOTPLUG + default m if DVB_FE_CUSTOMISE + select FW_LOADER + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + TODO: + This driver needs external firmware. Please use the command + "<kerneldir>/Documentation/dvb/get_dvb_firmware drx397xD" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + config DVB_L64781 tristate "LSI L64781" depends on DVB_CORE && I2C @@ -123,7 +158,7 @@ config DVB_L64781 config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -188,6 +223,14 @@ config DVB_DIB7000P A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. +config DVB_TDA10048 + tristate "Philips TDA10048HN based" + depends on DVB_CORE && I2C && HOTPLUG + default m if DVB_FE_CUSTOMISE + select FW_LOADER + help + A DVB-T tuner module. Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -224,7 +267,7 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" config DVB_NXT200X tristate "NxtWave Communications NXT2002/NXT2004 based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -239,7 +282,7 @@ config DVB_NXT200X config DVB_OR51211 tristate "Oren OR51211 based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -252,7 +295,7 @@ config DVB_OR51211 config DVB_OR51132 tristate "Oren OR51132 based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -268,7 +311,7 @@ config DVB_OR51132 config DVB_BCM3510 tristate "Broadcom BCM3510" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -291,65 +334,32 @@ config DVB_S5H1409 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. -comment "Tuners/PLL support" - depends on DVB_CORE - -config DVB_PLL - tristate "Generic I2C PLL based tuners" +config DVB_AU8522 + tristate "Auvitek AU8522 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help - This module drives a number of tuners based on PLL chips with a - common I2C interface. Say Y when you want to support these tuners. - -config DVB_TDA826X - tristate "Philips TDA826X silicon tuner" - depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE - help - A DVB-S silicon tuner module. Say Y when you want to support this tuner. + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. -config DVB_TDA827X - tristate "Philips TDA827X silicon tuner" +config DVB_S5H1411 + tristate "Samsung S5H1411 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help - A DVB-T silicon tuner module. Say Y when you want to support this tuner. + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. -config DVB_TDA18271 - tristate "NXP TDA18271 silicon tuner" - depends on I2C - default m if DVB_FE_CUSTOMISE - help - A silicon tuner module. Say Y when you want to support this tuner. +comment "Digital terrestrial only tuners/PLL" + depends on DVB_CORE -config DVB_TUNER_QT1010 - tristate "Quantek QT1010 silicon tuner" +config DVB_PLL + tristate "Generic I2C PLL based tuners" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help - A driver for the silicon tuner QT1010 from Quantek. - -config DVB_TUNER_MT2060 - tristate "Microtune MT2060 silicon IF tuner" - depends on I2C - default m if DVB_FE_CUSTOMISE - help - A driver for the silicon IF tuner MT2060 from Microtune. - -config DVB_TUNER_MT2266 - tristate "Microtune MT2266 silicon tuner" - depends on I2C - default m if DVB_FE_CUSTOMISE - help - A driver for the silicon baseband tuner MT2266 from Microtune. - -config DVB_TUNER_MT2131 - tristate "Microtune MT2131 silicon tuner" - depends on I2C - default m if DVB_FE_CUSTOMISE - help - A driver for the silicon baseband tuner MT2131 from Microtune. + This module drives a number of tuners based on PLL chips with a + common I2C interface. Say Y when you want to support these tuners. config DVB_TUNER_DIB0070 tristate "DiBcom DiB0070 silicon base-band tuner" @@ -360,21 +370,7 @@ config DVB_TUNER_DIB0070 This device is only used inside a SiP called togther with a demodulator for now. -config DVB_TUNER_XC5000 - tristate "Xceive XC5000 silicon tuner" - depends on I2C - default m if DVB_FE_CUSTOMISE - help - A driver for the silicon tuner XC5000 from Xceive. - This device is only used inside a SiP called togther with a - demodulator for now. - -config DVB_TUNER_ITD1000 - tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" - depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE - -comment "Miscellaneous devices" +comment "SEC control devices for DVB-S" depends on DVB_CORE config DVB_LNBP21 @@ -398,11 +394,4 @@ config DVB_ISL6421 help An SEC control chip. -config DVB_TUA6100 - tristate "TUA6100 PLL" - depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE - help - A DVBS PLL chip. - endmenu diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 129367886..028da5561 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -3,9 +3,7 @@ # EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -EXTRA_CFLAGS += -Idrivers/media/video/ - -tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o +EXTRA_CFLAGS += -Idrivers/media/common/tuners/ obj-$(CONFIG_DVB_PLL) += dvb-pll.o obj-$(CONFIG_DVB_STV0299) += stv0299.o @@ -27,6 +25,7 @@ obj-$(CONFIG_DVB_NXT6000) += nxt6000.o obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_ZL10353) += zl10353.o obj-$(CONFIG_DVB_CX22702) += cx22702.o +obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o obj-$(CONFIG_DVB_TDA10021) += tda10021.o obj-$(CONFIG_DVB_TDA10023) += tda10023.o obj-$(CONFIG_DVB_STV0297) += stv0297.o @@ -42,14 +41,10 @@ obj-$(CONFIG_DVB_ISL6405) += isl6405.o obj-$(CONFIG_DVB_ISL6421) += isl6421.o obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o -obj-$(CONFIG_DVB_TDA827X) += tda827x.o -obj-$(CONFIG_DVB_TDA18271) += tda18271.o -obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o -obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o -obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o -obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o -obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o +obj-$(CONFIG_DVB_AU8522) += au8522.o +obj-$(CONFIG_DVB_TDA10048) += tda10048.o +obj-$(CONFIG_DVB_S5H1411) += s5h1411.o diff --git a/linux/drivers/media/dvb/frontends/au8522.c b/linux/drivers/media/dvb/frontends/au8522.c new file mode 100644 index 000000000..3e1b0d96f --- /dev/null +++ b/linux/drivers/media/dvb/frontends/au8522.c @@ -0,0 +1,697 @@ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "au8522.h" + +struct au8522_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct au8522_config *config; + + struct dvb_frontend frontend; + + u32 current_frequency; + fe_modulation_t current_modulation; + +}; + +static int debug; + +#define dprintk(arg...) do { \ + if (debug) \ + printk(arg); \ + } while (0) + +/* 16 bit registers, 8 bit values */ +static int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) +{ + int ret; + u8 buf [] = { reg >> 8, reg & 0xff, data }; + + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u8 au8522_readreg(struct au8522_state *state, u16 reg) +{ + int ret; + u8 b0 [] = { reg >> 8, reg & 0xff }; + u8 b1 [] = { 0 }; + + struct i2c_msg msg [] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} + +static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return au8522_writereg(state, 0x106, 1); + else + return au8522_writereg(state, 0x106, 0); +} + +struct mse2snr_tab { + u16 val; + u16 data; +}; + +/* VSB SNR lookup table */ +static struct mse2snr_tab vsb_mse2snr_tab[] = { + { 0, 270 }, + { 2, 250 }, + { 3, 240 }, + { 5, 230 }, + { 7, 220 }, + { 9, 210 }, + { 12, 200 }, + { 13, 195 }, + { 15, 190 }, + { 17, 185 }, + { 19, 180 }, + { 21, 175 }, + { 24, 170 }, + { 27, 165 }, + { 31, 160 }, + { 32, 158 }, + { 33, 156 }, + { 36, 152 }, + { 37, 150 }, + { 39, 148 }, + { 40, 146 }, + { 41, 144 }, + { 43, 142 }, + { 44, 140 }, + { 48, 135 }, + { 50, 130 }, + { 43, 142 }, + { 53, 125 }, + { 56, 120 }, + { 256, 115 }, +}; + +/* QAM64 SNR lookup table */ +static struct mse2snr_tab qam64_mse2snr_tab[] = { + { 15, 0 }, + { 16, 290 }, + { 17, 288 }, + { 18, 286 }, + { 19, 284 }, + { 20, 282 }, + { 21, 281 }, + { 22, 279 }, + { 23, 277 }, + { 24, 275 }, + { 25, 273 }, + { 26, 271 }, + { 27, 269 }, + { 28, 268 }, + { 29, 266 }, + { 30, 264 }, + { 31, 262 }, + { 32, 260 }, + { 33, 259 }, + { 34, 258 }, + { 35, 256 }, + { 36, 255 }, + { 37, 254 }, + { 38, 252 }, + { 39, 251 }, + { 40, 250 }, + { 41, 249 }, + { 42, 248 }, + { 43, 246 }, + { 44, 245 }, + { 45, 244 }, + { 46, 242 }, + { 47, 241 }, + { 48, 240 }, + { 50, 239 }, + { 51, 238 }, + { 53, 237 }, + { 54, 236 }, + { 56, 235 }, + { 57, 234 }, + { 59, 233 }, + { 60, 232 }, + { 62, 231 }, + { 63, 230 }, + { 65, 229 }, + { 67, 228 }, + { 68, 227 }, + { 70, 226 }, + { 71, 225 }, + { 73, 224 }, + { 74, 223 }, + { 76, 222 }, + { 78, 221 }, + { 80, 220 }, + { 82, 219 }, + { 85, 218 }, + { 88, 217 }, + { 90, 216 }, + { 92, 215 }, + { 93, 214 }, + { 94, 212 }, + { 95, 211 }, + { 97, 210 }, + { 99, 209 }, + { 101, 208 }, + { 102, 207 }, + { 104, 206 }, + { 107, 205 }, + { 111, 204 }, + { 114, 203 }, + { 118, 202 }, + { 122, 201 }, + { 125, 200 }, + { 128, 199 }, + { 130, 198 }, + { 132, 197 }, + { 256, 190 }, +}; + +/* QAM256 SNR lookup table */ +static struct mse2snr_tab qam256_mse2snr_tab[] = { + { 16, 0 }, + { 17, 400 }, + { 18, 398 }, + { 19, 396 }, + { 20, 394 }, + { 21, 392 }, + { 22, 390 }, + { 23, 388 }, + { 24, 386 }, + { 25, 384 }, + { 26, 382 }, + { 27, 380 }, + { 28, 379 }, + { 29, 378 }, + { 30, 377 }, + { 31, 376 }, + { 32, 375 }, + { 33, 374 }, + { 34, 373 }, + { 35, 372 }, + { 36, 371 }, + { 37, 370 }, + { 38, 362 }, + { 39, 354 }, + { 40, 346 }, + { 41, 338 }, + { 42, 330 }, + { 43, 328 }, + { 44, 326 }, + { 45, 324 }, + { 46, 322 }, + { 47, 320 }, + { 48, 319 }, + { 49, 318 }, + { 50, 317 }, + { 51, 316 }, + { 52, 315 }, + { 53, 314 }, + { 54, 313 }, + { 55, 312 }, + { 56, 311 }, + { 57, 310 }, + { 58, 308 }, + { 59, 306 }, + { 60, 304 }, + { 61, 302 }, + { 62, 300 }, + { 63, 298 }, + { 65, 295 }, + { 68, 294 }, + { 70, 293 }, + { 73, 292 }, + { 76, 291 }, + { 78, 290 }, + { 79, 289 }, + { 81, 288 }, + { 82, 287 }, + { 83, 286 }, + { 84, 285 }, + { 85, 284 }, + { 86, 283 }, + { 88, 282 }, + { 89, 281 }, + { 256, 280 }, +}; + +static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse, + u16 *snr) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < sz; i++) { + if (mse < tab[i].val) { + *snr = tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __func__, *snr); + return ret; +} + +/* VSB Modulation table */ +static struct { + u16 reg; + u16 data; +} VSB_mod_tab[] = { + { 0x8090, 0x84 }, + { 0x4092, 0x11 }, + { 0x2005, 0x00 }, + { 0x8091, 0x80 }, + { 0x80a3, 0x0c }, + { 0x80a4, 0xe8 }, + { 0x8081, 0xc4 }, + { 0x80a5, 0x40 }, + { 0x80a7, 0x40 }, + { 0x80a6, 0x67 }, + { 0x8262, 0x20 }, + { 0x821c, 0x30 }, + { 0x80d8, 0x1a }, + { 0x8227, 0xa0 }, + { 0x8121, 0xff }, + { 0x80a8, 0xf0 }, + { 0x80a9, 0x05 }, + { 0x80aa, 0x77 }, + { 0x80ab, 0xf0 }, + { 0x80ac, 0x05 }, + { 0x80ad, 0x77 }, + { 0x80ae, 0x41 }, + { 0x80af, 0x66 }, + { 0x821b, 0xcc }, + { 0x821d, 0x80 }, + { 0x80b5, 0xfb }, + { 0x80b6, 0x8e }, + { 0x80b7, 0x39 }, + { 0x80a4, 0xe8 }, + { 0x8231, 0x13 }, +}; + +/* QAM Modulation table */ +static struct { + u16 reg; + u16 data; +} QAM_mod_tab[] = { + { 0x80a3, 0x09 }, + { 0x80a4, 0x00 }, + { 0x8081, 0xc4 }, + { 0x80a5, 0x40 }, + { 0x80b5, 0xfb }, + { 0x80b6, 0x8e }, + { 0x80b7, 0x39 }, + { 0x80aa, 0x77 }, + { 0x80ad, 0x77 }, + { 0x80a6, 0x67 }, + { 0x8262, 0x20 }, + { 0x821c, 0x30 }, + { 0x80b8, 0x3e }, + { 0x80b9, 0xf0 }, + { 0x80ba, 0x01 }, + { 0x80bb, 0x18 }, + { 0x80bc, 0x50 }, + { 0x80bd, 0x00 }, + { 0x80be, 0xea }, + { 0x80bf, 0xef }, + { 0x80c0, 0xfc }, + { 0x80c1, 0xbd }, + { 0x80c2, 0x1f }, + { 0x80c3, 0xfc }, + { 0x80c4, 0xdd }, + { 0x80c5, 0xaf }, + { 0x80c6, 0x00 }, + { 0x80c7, 0x38 }, + { 0x80c8, 0x30 }, + { 0x80c9, 0x05 }, + { 0x80ca, 0x4a }, + { 0x80cb, 0xd0 }, + { 0x80cc, 0x01 }, + { 0x80cd, 0xd9 }, + { 0x80ce, 0x6f }, + { 0x80cf, 0xf9 }, + { 0x80d0, 0x70 }, + { 0x80d1, 0xdf }, + { 0x80d2, 0xf7 }, + { 0x80d3, 0xc2 }, + { 0x80d4, 0xdf }, + { 0x80d5, 0x02 }, + { 0x80d6, 0x9a }, + { 0x80d7, 0xd0 }, + { 0x8250, 0x0d }, + { 0x8251, 0xcd }, + { 0x8252, 0xe0 }, + { 0x8253, 0x05 }, + { 0x8254, 0xa7 }, + { 0x8255, 0xff }, + { 0x8256, 0xed }, + { 0x8257, 0x5b }, + { 0x8258, 0xae }, + { 0x8259, 0xe6 }, + { 0x825a, 0x3d }, + { 0x825b, 0x0f }, + { 0x825c, 0x0d }, + { 0x825d, 0xea }, + { 0x825e, 0xf2 }, + { 0x825f, 0x51 }, + { 0x8260, 0xf5 }, + { 0x8261, 0x06 }, + { 0x821a, 0x00 }, + { 0x8546, 0x40 }, + { 0x8210, 0x26 }, + { 0x8211, 0xf6 }, + { 0x8212, 0x84 }, + { 0x8213, 0x02 }, + { 0x8502, 0x01 }, + { 0x8121, 0x04 }, + { 0x8122, 0x04 }, + { 0x852e, 0x10 }, + { 0x80a4, 0xca }, + { 0x80a7, 0x40 }, + { 0x8526, 0x01 }, +}; + +static int au8522_enable_modulation(struct dvb_frontend *fe, + fe_modulation_t m) +{ + struct au8522_state *state = fe->demodulator_priv; + int i; + + dprintk("%s(0x%08x)\n", __func__, m); + + switch (m) { + case VSB_8: + dprintk("%s() VSB_8\n", __func__); + for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++) + au8522_writereg(state, + VSB_mod_tab[i].reg, + VSB_mod_tab[i].data); + break; + case QAM_64: + case QAM_256: + dprintk("%s() QAM 64/256\n", __func__); + for (i = 0; i < ARRAY_SIZE(QAM_mod_tab); i++) + au8522_writereg(state, + QAM_mod_tab[i].reg, + QAM_mod_tab[i].data); + break; + default: + dprintk("%s() Invalid modulation\n", __func__); + return -EINVAL; + } + + state->current_modulation = m; + + return 0; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int au8522_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(frequency=%d)\n", __func__, p->frequency); + + state->current_frequency = p->frequency; + + au8522_enable_modulation(fe, p->u.vsb.modulation); + + /* Allow the demod to settle */ + msleep(100); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int au8522_init(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + au8522_writereg(state, 0xa4, 1 << 5); + + au8522_i2c_gate_ctrl(fe, 1); + + return 0; +} + +static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct au8522_state *state = fe->demodulator_priv; + u8 reg; + u32 tuner_status = 0; + + *status = 0; + + if (state->current_modulation == VSB_8) { + dprintk("%s() Checking VSB_8\n", __func__); + reg = au8522_readreg(state, 0x4088); + if (reg & 0x01) + *status |= FE_HAS_VITERBI; + if (reg & 0x02) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + } else { + dprintk("%s() Checking QAM\n", __func__); + reg = au8522_readreg(state, 0x4541); + if (reg & 0x80) + *status |= FE_HAS_VITERBI; + if (reg & 0x20) + *status |= FE_HAS_LOCK | FE_HAS_SYNC; + } + + switch (state->config->status_mode) { + case AU8522_DEMODLOCKING: + dprintk("%s() DEMODLOCKING\n", __func__); + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case AU8522_TUNERLOCKING: + /* Get the tuner status */ + dprintk("%s() TUNERLOCKING\n", __func__); + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + + dprintk("%s() status 0x%08x\n", __func__, *status); + + return 0; +} + +static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; + + dprintk("%s()\n", __func__); + + if (state->current_modulation == QAM_256) + ret = au8522_mse2snr_lookup(qam256_mse2snr_tab, + ARRAY_SIZE(qam256_mse2snr_tab), + au8522_readreg(state, 0x4522), + snr); + else if (state->current_modulation == QAM_64) + ret = au8522_mse2snr_lookup(qam64_mse2snr_tab, + ARRAY_SIZE(qam64_mse2snr_tab), + au8522_readreg(state, 0x4522), + snr); + else /* VSB_8 */ + ret = au8522_mse2snr_lookup(vsb_mse2snr_tab, + ARRAY_SIZE(vsb_mse2snr_tab), + au8522_readreg(state, 0x4311), + snr); + + return ret; +} + +static int au8522_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + return au8522_read_snr(fe, signal_strength); +} + +static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct au8522_state *state = fe->demodulator_priv; + + if (state->current_modulation == VSB_8) + *ucblocks = au8522_readreg(state, 0x4087); + else + *ucblocks = au8522_readreg(state, 0x4543); + + return 0; +} + +static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return au8522_read_ucblocks(fe, ber); +} + +static int au8522_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct au8522_state *state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + p->u.vsb.modulation = state->current_modulation; + + return 0; +} + +static int au8522_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void au8522_release(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops au8522_ops; + +struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c) +{ + struct au8522_state *state = NULL; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct au8522_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; +#if 0 + /* check if the demod exists */ + reg = au8522_readreg(state, 0x04); + if ((reg != 0x0066) && (reg != 0x007f)) + goto error; +#endif + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &au8522_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + if (au8522_init(&state->frontend) != 0) { + printk(KERN_ERR "%s: Failed to initialize correctly\n", + __func__); + goto error; + } + + /* Note: Leaving the I2C gate open here. */ + au8522_i2c_gate_ctrl(&state->frontend, 1); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(au8522_attach); + +static struct dvb_frontend_ops au8522_ops = { + + .info = { + .name = "Auvitek AU8522 QAM/8VSB Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = au8522_init, + .i2c_gate_ctrl = au8522_i2c_gate_ctrl, + .set_frontend = au8522_set_frontend, + .get_frontend = au8522_get_frontend, + .get_tune_settings = au8522_get_tune_settings, + .read_status = au8522_read_status, + .read_ber = au8522_read_ber, + .read_signal_strength = au8522_read_signal_strength, + .read_snr = au8522_read_snr, + .read_ucblocks = au8522_read_ucblocks, + .release = au8522_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/au8522.h b/linux/drivers/media/dvb/frontends/au8522.h new file mode 100644 index 000000000..d7affa3cd --- /dev/null +++ b/linux/drivers/media/dvb/frontends/au8522.h @@ -0,0 +1,56 @@ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __AU8522_H__ +#define __AU8522_H__ + +#include <linux/dvb/frontend.h> + +struct au8522_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* Return lock status based on tuner lock, or demod lock */ +#define AU8522_TUNERLOCKING 0 +#define AU8522_DEMODLOCKING 1 + u8 status_mode; +}; + +#if defined(CONFIG_DVB_AU8522) || \ + (defined(CONFIG_DVB_AU8522_MODULE) && defined(MODULE)) +extern struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *au8522_attach(const struct au8522_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_AU8522 */ + +#endif /* __AU8522_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + */ diff --git a/linux/drivers/media/dvb/frontends/bsbe1.h b/linux/drivers/media/dvb/frontends/bsbe1.h index d8f65738e..5e431ebd0 100644 --- a/linux/drivers/media/dvb/frontends/bsbe1.h +++ b/linux/drivers/media/dvb/frontends/bsbe1.h @@ -1,5 +1,5 @@ /* - * bsbe1.h - ALPS BSBE1 tuner support (moved from av7110.c) + * bsbe1.h - ALPS BSBE1 tuner support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,44 +26,24 @@ #define BSBE1_H static u8 alps_bsbe1_inittab[] = { - 0x01, 0x15, - 0x02, 0x30, - 0x03, 0x00, + 0x01, 0x15, /* XTAL = 4MHz, VCO = 352 MHz */ + 0x02, 0x30, /* MCLK = 88 MHz */ + 0x03, 0x00, /* ACR output 0 */ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ - 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ - 0x06, 0x40, /* DAC not used, set to high impendance mode */ - 0x07, 0x00, /* DAC LSB */ + 0x05, 0x05, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x00, /* DAC output 0 */ 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ 0x09, 0x00, /* FIFO */ - 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ - 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ - 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ - 0x10, 0x3f, // AGC2 0x3d - 0x11, 0x84, - 0x12, 0xb9, - 0x15, 0xc9, // lock detector threshold - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 - 0x29, 0x1e, // 1/2 threshold - 0x2a, 0x14, // 2/3 threshold - 0x2b, 0x0f, // 3/4 threshold - 0x2c, 0x09, // 5/6 threshold - 0x2d, 0x05, // 7/8 threshold - 0x2e, 0x01, - 0x31, 0x1f, // test all FECs - 0x32, 0x19, // viterbi and synchro search - 0x33, 0xfc, // rs control - 0x34, 0x93, // error control - 0x0f, 0x92, + 0x0c, 0x51, /* OP1/OP0 normal, val = 1 (LNB power on) */ + 0x0d, 0x82, /* DC offset compensation = on, beta_agc1 = 2 */ + 0x0f, 0x92, /* AGC1R */ + 0x10, 0x34, /* AGC2O */ + 0x11, 0x84, /* TLSR */ + 0x12, 0xb9, /* CFD */ + 0x15, 0xc9, /* lock detector threshold */ + 0x28, 0x00, /* out imp: normal, type: parallel, FEC mode: QPSK */ + 0x33, 0xfc, /* RS control */ + 0x34, 0x93, /* count viterbi bit errors per 2E18 bytes */ 0xff, 0xff }; @@ -100,11 +80,11 @@ static int alps_bsbe1_tuner_set_params(struct dvb_frontend* fe, struct dvb_front if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; - div = (params->frequency + (125 - 1)) / 125; // round correctly + div = params->frequency / 1000; data[0] = (div >> 8) & 0x7f; data[1] = div & 0xff; - data[2] = 0x80 | ((div & 0x18000) >> 10) | 4; - data[3] = (params->frequency > 1530000) ? 0xE0 : 0xE4; + data[2] = 0x80 | ((div & 0x18000) >> 10) | 0x1; + data[3] = 0xe0; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); diff --git a/linux/drivers/media/dvb/frontends/bsru6.h b/linux/drivers/media/dvb/frontends/bsru6.h index e231cd84b..45a6dfd8e 100644 --- a/linux/drivers/media/dvb/frontends/bsru6.h +++ b/linux/drivers/media/dvb/frontends/bsru6.h @@ -133,7 +133,7 @@ static struct stv0299_config alps_bsru6_config = { .mclk = 88000000UL, .invert = 1, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_1, + .lock_output = STV0299_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = alps_bsru6_set_symbol_rate, diff --git a/linux/drivers/media/dvb/frontends/dib7000p.c b/linux/drivers/media/dvb/frontends/dib7000p.c index 9b6702b70..c74d373b6 100644 --- a/linux/drivers/media/dvb/frontends/dib7000p.c +++ b/linux/drivers/media/dvb/frontends/dib7000p.c @@ -1194,7 +1194,7 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe, ret = dib7000p_tune(fe, fep); /* make this a config parameter */ - dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO); + dib7000p_set_output_mode(state, state->cfg.output_mode); return ret; } @@ -1356,6 +1356,12 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, st->gpio_val = cfg->gpio_val; st->gpio_dir = cfg->gpio_dir; + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if (st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) + st->cfg.output_mode = OUTMODE_MPEG2_FIFO; + demod = &st->demod; demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); diff --git a/linux/drivers/media/dvb/frontends/dib7000p.h b/linux/drivers/media/dvb/frontends/dib7000p.h index eefcac8b5..07c4d12ed 100644 --- a/linux/drivers/media/dvb/frontends/dib7000p.h +++ b/linux/drivers/media/dvb/frontends/dib7000p.h @@ -31,11 +31,26 @@ struct dib7000p_config { u8 spur_protect; int (*agc_control) (struct dvb_frontend *, u8 before); + + u8 output_mode; }; #define DEFAULT_DIB7000P_I2C_ADDRESS 18 -extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && defined(MODULE)) +extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, + struct dib7000p_config *cfg); +#else +static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, + u8 i2c_addr, + struct dib7000p_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); diff --git a/linux/drivers/media/dvb/frontends/drx397xD.c b/linux/drivers/media/dvb/frontends/drx397xD.c new file mode 100644 index 000000000..327fff4b8 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/drx397xD.c @@ -0,0 +1,1583 @@ +/* + * Driver for Micronas drx397xD demodulator + * + * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#define DEBUG /* uncomment if you want debugging output */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/firmware.h> +#include <asm/div64.h> + +#include "dvb_frontend.h" +#include "drx397xD.h" +#include "compat.h" + +static const char mod_name[] = "drx397xD"; + +#define MAX_CLOCK_DRIFT 200 /* maximal 200 PPM allowed */ + +#define F_SET_0D0h 1 +#define F_SET_0D4h 2 + +typedef enum fw_ix { +#define _FW_ENTRY(a, b) b +#include "drx397xD_fw.h" +} fw_ix_t; + +/* chip specifics */ +struct drx397xD_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct drx397xD_config config; + fw_ix_t chip_rev; + int flags; + u32 bandwidth_parm; /* internal bandwidth conversions */ + u32 f_osc; /* w90: actual osc frequency [Hz] */ +}; + +/******************************************************************************* + * Firmware + ******************************************************************************/ + +static const char *blob_name[] = { +#define _BLOB_ENTRY(a, b) a +#include "drx397xD_fw.h" +}; + +typedef enum blob_ix { +#define _BLOB_ENTRY(a, b) b +#include "drx397xD_fw.h" +} blob_ix_t; + +static struct { + const char *name; + const struct firmware *file; + rwlock_t lock; + int refcnt; + u8 *data[ARRAY_SIZE(blob_name)]; +} fw[] = { +#define _FW_ENTRY(a, b) { \ + .name = a, \ + .file = 0, \ + .lock = RW_LOCK_UNLOCKED, \ + .refcnt = 0, \ + .data = { } } +#include "drx397xD_fw.h" +}; + +/* use only with writer lock aquired */ +static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix) +{ + memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); + if (fw[ix].file) + release_firmware(fw[ix].file); +} + +static void drx_release_fw(struct drx397xD_state *s) +{ + fw_ix_t ix = s->chip_rev; + + pr_debug("%s\n", __FUNCTION__); + + write_lock(&fw[ix].lock); + if (fw[ix].refcnt) { + fw[ix].refcnt--; + if (fw[ix].refcnt == 0) + _drx_release_fw(s, ix); + } + write_unlock(&fw[ix].lock); +} + +static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix) +{ + u8 *data; + size_t size, len; + int i = 0, j, rc = -EINVAL; + + pr_debug("%s\n", __FUNCTION__); + + if (ix < 0 || ix >= ARRAY_SIZE(fw)) + return -EINVAL; + s->chip_rev = ix; + + write_lock(&fw[ix].lock); + if (fw[ix].file) { + rc = 0; + goto exit_ok; + } + memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); + + if (request_firmware(&fw[ix].file, fw[ix].name, &s->i2c->dev) != 0) { + printk(KERN_ERR "%s: Firmware \"%s\" not available\n", + mod_name, fw[ix].name); + rc = -ENOENT; + goto exit_err; + } + + if (!fw[ix].file->data || fw[ix].file->size < 10) + goto exit_corrupt; + + data = fw[ix].file->data; + size = fw[ix].file->size; + + if (data[i++] != 2) /* check firmware version */ + goto exit_corrupt; + + do { + switch (data[i++]) { + case 0x00: /* bytecode */ + if (i >= size) + break; + i += data[i]; + case 0x01: /* reset */ + case 0x02: /* sleep */ + i++; + break; + case 0xfe: /* name */ + len = strnlen(&data[i], size - i); + if (i + len + 1 >= size) + goto exit_corrupt; + if (data[i + len + 1] != 0) + goto exit_corrupt; + for (j = 0; j < ARRAY_SIZE(blob_name); j++) { + if (strcmp(blob_name[j], &data[i]) == 0) { + fw[ix].data[j] = &data[i + len + 1]; + pr_debug("Loading %s\n", blob_name[j]); + } + } + i += len + 1; + break; + case 0xff: /* file terminator */ + if (i == size) { + rc = 0; + goto exit_ok; + } + default: + goto exit_corrupt; + } + } while (i < size); + exit_corrupt: + printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name); + exit_err: + _drx_release_fw(s, ix); + fw[ix].refcnt--; + exit_ok: + fw[ix].refcnt++; + write_unlock(&fw[ix].lock); + return rc; +} + +/******************************************************************************* + * i2c bus IO + ******************************************************************************/ + +static int write_fw(struct drx397xD_state *s, blob_ix_t ix) +{ + struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 }; + u8 *data; + int len, rc = 0, i = 0; + + if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) { + pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__); + return -EINVAL; + } + pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]); + + read_lock(&fw[s->chip_rev].lock); + data = fw[s->chip_rev].data[ix]; + if (!data) { + rc = -EINVAL; + goto exit_rc; + } + + for (;;) { + switch (data[i++]) { + case 0: /* bytecode */ + len = data[i++]; + msg.len = len; + msg.buf = &data[i]; + if (i2c_transfer(s->i2c, &msg, 1) != 1) { + rc = -EIO; + goto exit_rc; + } + i += len; + break; + case 1: /* reset */ + case 2: /* sleep */ + i++; + break; + default: + goto exit_rc; + } + } + exit_rc: + read_unlock(&fw[s->chip_rev].lock); + return 0; +} + +/* Function is not endian safe, use the RD16 wrapper below */ +static int _read16(struct drx397xD_state *s, u32 i2c_adr) +{ + int rc; + u8 a[4]; + u16 v; + struct i2c_msg msg[2] = { + { + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + } + , { + .addr = s->config.demod_address, + .flags = I2C_M_RD, + .buf = (u8 *) & v, + .len = sizeof(v) + } + }; + + *(u32 *) a = i2c_adr; + + rc = i2c_transfer(s->i2c, msg, 2); + if (rc != 2) + return -EIO; + + return le16_to_cpu(v); +} + +/* Function is not endian safe, use the WR16.. wrappers below */ +static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val) +{ + u8 a[6]; + int rc; + struct i2c_msg msg = { + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + }; + + *(u32 *) a = i2c_adr; + *(u16 *) & a[4] = val; + + rc = i2c_transfer(s->i2c, &msg, 1); + if (rc != 1) + return -EIO; + return 0; +} + +#define WR16(ss,adr, val) \ + _write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val)) +#define WR16_E0(ss,adr, val) \ + _write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val)) +#define RD16(ss,adr) \ + _read16(ss, I2C_ADR_C0(adr)) + +#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc + +/******************************************************************************* + * Tuner callback + ******************************************************************************/ + +static int PLL_Set(struct drx397xD_state *s, + struct dvb_frontend_parameters *fep, int *df_tuner) +{ + struct dvb_frontend *fe = &s->frontend; + u32 f_tuner, f = fep->frequency; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + if ((f > s->frontend.ops.tuner_ops.info.frequency_max) || + (f < s->frontend.ops.tuner_ops.info.frequency_min)) + return -EINVAL; + + *df_tuner = 0; + if (!s->frontend.ops.tuner_ops.set_params || + !s->frontend.ops.tuner_ops.get_frequency) + return -ENOSYS; + + rc = s->frontend.ops.tuner_ops.set_params(fe, fep); + if (rc < 0) + return rc; + + rc = s->frontend.ops.tuner_ops.get_frequency(fe, &f_tuner); + if (rc < 0) + return rc; + + *df_tuner = f_tuner - f; + pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f, + f_tuner); + + return 0; +} + +/******************************************************************************* + * Demodulator helper functions + ******************************************************************************/ + +static int SC_WaitForReady(struct drx397xD_state *s) +{ + int cnt = 1000; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + while (cnt--) { + rc = RD16(s, 0x820043); + if (rc == 0) + return 0; + } + return -1; +} + +static int SC_SendCommand(struct drx397xD_state *s, int cmd) +{ + int rc; + + pr_debug("%s\n", __FUNCTION__); + + WR16(s, 0x820043, cmd); + SC_WaitForReady(s); + rc = RD16(s, 0x820042); + if ((rc & 0xffff) == 0xffff) + return -1; + return 0; +} + +static int HI_Command(struct drx397xD_state *s, u16 cmd) +{ + int rc, cnt = 1000; + + pr_debug("%s\n", __FUNCTION__); + + rc = WR16(s, 0x420032, cmd); + if (rc < 0) + return rc; + + do { + rc = RD16(s, 0x420032); + if (rc == 0) { + rc = RD16(s, 0x420031); + return rc; + } + if (rc < 0) + return rc; + } while (--cnt); + return rc; +} + +static int HI_CfgCommand(struct drx397xD_state *s) +{ + + pr_debug("%s\n", __FUNCTION__); + + WR16(s, 0x420033, 0x3973); + WR16(s, 0x420034, s->config.w50); // code 4, log 4 + WR16(s, 0x420035, s->config.w52); // code 15, log 9 + WR16(s, 0x420036, s->config.demod_address << 1); + WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1 +// WR16(s, 0x420033, 0x3973); + if ((s->config.w56 & 8) == 0) + return HI_Command(s, 3); + return WR16(s, 0x420032, 0x3); +} + +static const u8 fastIncrDecLUT_15273[] = { + 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f +}; + +static const u8 slowIncrDecLUT_15272[] = { + 3, 4, 4, 5, 6 +}; + +static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc) +{ + u16 w06 = agc->w06; + u16 w08 = agc->w08; + u16 w0A = agc->w0A; + u16 w0C = agc->w0C; + int quot, rem, i, rc = -EINVAL; + + pr_debug("%s\n", __FUNCTION__); + + if (agc->w04 > 0x3ff) + goto exit_rc; + + if (agc->d00 == 1) { + EXIT_RC(RD16(s, 0x0c20010)); + rc &= ~0x10; + EXIT_RC(WR16(s, 0x0c20010, rc)); + return WR16(s, 0x0c20030, agc->w04 & 0x7ff); + } + + if (agc->d00 != 0) + goto exit_rc; + if (w0A < w08) + goto exit_rc; + if (w0A > 0x3ff) + goto exit_rc; + if (w0C > 0x3ff) + goto exit_rc; + if (w06 > 0x3ff) + goto exit_rc; + + EXIT_RC(RD16(s, 0x0c20010)); + rc |= 0x10; + EXIT_RC(WR16(s, 0x0c20010, rc)); + + EXIT_RC(WR16(s, 0x0c20025, (w06 >> 1) & 0x1ff)); + EXIT_RC(WR16(s, 0x0c20031, (w0A - w08) >> 1)); + EXIT_RC(WR16(s, 0x0c20032, ((w0A + w08) >> 1) - 0x1ff)); + + quot = w0C / 113; + rem = w0C % 113; + if (quot <= 8) { + quot = 8 - quot; + } else { + quot = 0; + rem += 113; + } + + EXIT_RC(WR16(s, 0x0c20024, quot)); + + i = fastIncrDecLUT_15273[rem / 8]; + EXIT_RC(WR16(s, 0x0c2002d, i)); + EXIT_RC(WR16(s, 0x0c2002e, i)); + + i = slowIncrDecLUT_15272[rem / 28]; + EXIT_RC(WR16(s, 0x0c2002b, i)); + rc = WR16(s, 0x0c2002c, i); + exit_rc: + return rc; +} + +static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc) +{ + u16 w04 = agc->w04; + u16 w06 = agc->w06; + int rc = -1; + + pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06); + + if (w04 > 0x3ff) + goto exit_rc; + + switch (agc->d00) { + case 1: + if (w04 == 0x3ff) + w04 = 0x400; + + EXIT_RC(WR16(s, 0x0c20036, w04)); + s->config.w9C &= ~2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + EXIT_RC(WR16(s, 0x0c20010, rc)); + EXIT_RC(RD16(s, 0x0c20013)); + rc &= ~2; + break; + case 0: + // loc_8000659 + s->config.w9C &= ~2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + rc |= 0x4000; + EXIT_RC(WR16(s, 0x0c20010, rc)); + EXIT_RC(WR16(s, 0x0c20051, (w06 >> 4) & 0x3f)); + EXIT_RC(RD16(s, 0x0c20013)); + rc &= ~2; + break; + default: + s->config.w9C |= 2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + EXIT_RC(WR16(s, 0x0c20010, rc)); + + EXIT_RC(WR16(s, 0x0c20036, 0)); + + EXIT_RC(RD16(s, 0x0c20013)); + rc |= 2; + } + rc = WR16(s, 0x0c20013, rc); + exit_rc: + return rc; +} + +static int GetLockStatus(struct drx397xD_state *s, int *lockstat) +{ + int rc; + + *lockstat = 0; + + rc = RD16(s, 0x082004b); + if (rc < 0) + return rc; + + if (s->config.d60 != 2) + return 0; + + if ((rc & 7) == 7) + *lockstat |= 1; + if ((rc & 3) == 3) + *lockstat |= 2; + if (rc & 1) + *lockstat |= 4; + return 0; +} + +static int CorrectSysClockDeviation(struct drx397xD_state *s) +{ + int rc = -EINVAL; + int lockstat; + u32 clk, clk_limit; + + pr_debug("%s\n", __FUNCTION__); + + if (s->config.d5C == 0) { + EXIT_RC(WR16(s, 0x08200e8, 0x010)); + EXIT_RC(WR16(s, 0x08200e9, 0x113)); + s->config.d5C = 1; + return rc; + } + if (s->config.d5C != 1) + goto exit_rc; + + rc = RD16(s, 0x0820048); + + rc = GetLockStatus(s, &lockstat); + if (rc < 0) + goto exit_rc; + if ((lockstat & 1) == 0) + goto exit_rc; + + EXIT_RC(WR16(s, 0x0420033, 0x200)); + EXIT_RC(WR16(s, 0x0420034, 0xc5)); + EXIT_RC(WR16(s, 0x0420035, 0x10)); + EXIT_RC(WR16(s, 0x0420036, 0x1)); + EXIT_RC(WR16(s, 0x0420037, 0xa)); + EXIT_RC(HI_Command(s, 6)); + EXIT_RC(RD16(s, 0x0420040)); + clk = rc; + EXIT_RC(RD16(s, 0x0420041)); + clk |= rc << 16; + + if (clk <= 0x26ffff) + goto exit_rc; + if (clk > 0x610000) + goto exit_rc; + + if (!s->bandwidth_parm) + return -EINVAL; + + /* round & convert to Hz */ + clk = ((u64) (clk + 0x800000) * s->bandwidth_parm + (1 << 20)) >> 21; + clk_limit = s->config.f_osc * MAX_CLOCK_DRIFT / 1000; + + if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) { + s->f_osc = clk; + pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__, + s->config.f_osc * 1000, clk - s->config.f_osc * 1000); + } + rc = WR16(s, 0x08200e8, 0); + exit_rc: + return rc; +} + +static int ConfigureMPEGOutput(struct drx397xD_state *s, int type) +{ + int rc, si, bp; + + pr_debug("%s\n", __FUNCTION__); + + si = s->config.wA0; + if (s->config.w98 == 0) { + si |= 1; + bp = 0; + } else { + si &= ~1; + bp = 0x200; + } + if (s->config.w9A == 0) { + si |= 0x80; + } else { + si &= ~0x80; + } + + EXIT_RC(WR16(s, 0x2150045, 0)); + EXIT_RC(WR16(s, 0x2150010, si)); + EXIT_RC(WR16(s, 0x2150011, bp)); + rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0)); + exit_rc: + return rc; +} + +static int drx_tune(struct drx397xD_state *s, + struct dvb_frontend_parameters *fep) +{ + u16 v22 = 0; + u16 v1C = 0; + u16 v1A = 0; + u16 v18 = 0; + u32 edi = 0, ebx = 0, ebp = 0, edx = 0; + u16 v20 = 0, v1E = 0, v16 = 0, v14 = 0, v12 = 0, v10 = 0, v0E = 0; + + int rc, df_tuner; + int a, b, c, d; + pr_debug("%s %d\n", __FUNCTION__, s->config.d60); + + if (s->config.d60 != 2) + goto set_tuner; + rc = CorrectSysClockDeviation(s); + if (rc < 0) + goto set_tuner; + + s->config.d60 = 1; + rc = ConfigureMPEGOutput(s, 0); + if (rc < 0) + goto set_tuner; +#if 0 + + if (s->chip_rev != DRXD_FW_B1) { + rc = WR16(s, 0x2150000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x2110000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x0800000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x2800000, 0); + } else { + rc = WR16(s, 0x0800000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x2800000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x1000000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x1400000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x1800000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x1c00000, 0); + if (rc < 0) + goto loc_800111A; + rc = WR16(s, 0x2000000, 0); + } + loc_800111A: +#endif + set_tuner: + + rc = PLL_Set(s, fep, &df_tuner); + if (rc < 0) { + printk(KERN_ERR "Error in pll_set\n"); + goto exit_rc; + } + msleep(200); + + a = rc = RD16(s, 0x2150016); + if (rc < 0) + goto exit_rc; + b = rc = RD16(s, 0x2150010); + if (rc < 0) + goto exit_rc; + c = rc = RD16(s, 0x2150034); + if (rc < 0) + goto exit_rc; + d = rc = RD16(s, 0x2150035); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2150014, c); + rc = WR16(s, 0x2150015, d); + rc = WR16(s, 0x2150010, 0); + rc = WR16(s, 0x2150000, 2); + rc = WR16(s, 0x2150036, 0x0fff); + rc = WR16(s, 0x2150016, a); + + rc = WR16(s, 0x2150010, 2); + rc = WR16(s, 0x2150007, 0); + rc = WR16(s, 0x2150000, 1); + rc = WR16(s, 0x2110000, 0); + rc = WR16(s, 0x0800000, 0); + rc = WR16(s, 0x2800000, 0); + rc = WR16(s, 0x2110010, 0x664); + + rc = write_fw(s, DRXD_ResetECRAM); + rc = WR16(s, 0x2110000, 1); + + rc = write_fw(s, DRXD_InitSC); + if (rc < 0) + goto exit_rc; + + rc = SetCfgIfAgc(s, &s->config.ifagc); + if (rc < 0) + goto exit_rc; + + rc = SetCfgRfAgc(s, &s->config.rfagc); + if (rc < 0) + goto exit_rc; + + if (fep->u.ofdm.transmission_mode != TRANSMISSION_MODE_2K) + v22 = 1; + switch (fep->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_8K: + edi = 1; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x2010010, 0); + if (rc < 0) + break; + v1C = 0x63; + v1A = 0x53; + v18 = 0x43; + break; + default: + edi = 0; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x2010010, 1); + if (rc < 0) + break; + + v1C = 0x61; + v1A = 0x47; + v18 = 0x41; + } + + switch (fep->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_4: + edi |= 0x0c; + break; + case GUARD_INTERVAL_1_8: + edi |= 0x08; + break; + case GUARD_INTERVAL_1_16: + edi |= 0x04; + break; + case GUARD_INTERVAL_1_32: + break; + default: + v22 |= 2; + } + + ebx = 0; + ebp = 0; + v20 = 0; + v1E = 0; + v16 = 0; + v14 = 0; + v12 = 0; + v10 = 0; + v0E = 0; + + switch (fep->u.ofdm.hierarchy_information) { + case HIERARCHY_1: + edi |= 0x40; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 1); + if (rc < 0) + goto exit_rc; + ebx = 0x19f; + ebp = 0x1fb; + v20 = 0x0c0; + v1E = 0x195; + v16 = 0x1d6; + v14 = 0x1ef; + v12 = 4; + v10 = 5; + v0E = 5; + break; + case HIERARCHY_2: + edi |= 0x80; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 2); + if (rc < 0) + goto exit_rc; + ebx = 0x08f; + ebp = 0x12f; + v20 = 0x0c0; + v1E = 0x11e; + v16 = 0x1d6; + v14 = 0x15e; + v12 = 4; + v10 = 5; + v0E = 5; + break; + case HIERARCHY_4: + edi |= 0xc0; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 3); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 3); + if (rc < 0) + goto exit_rc; + ebx = 0x14d; + ebp = 0x197; + v20 = 0x0c0; + v1E = 0x1ce; + v16 = 0x1d6; + v14 = 0x11a; + v12 = 4; + v10 = 6; + v0E = 5; + break; + default: + v22 |= 8; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 0); + if (rc < 0) + goto exit_rc; + // QPSK QAM16 QAM64 + ebx = 0x19f; // 62 + ebp = 0x1fb; // 15 + v20 = 0x16a; // 62 + v1E = 0x195; // 62 + v16 = 0x1bb; // 15 + v14 = 0x1ef; // 15 + v12 = 5; // 16 + v10 = 5; // 16 + v0E = 5; // 16 + } + + switch (fep->u.ofdm.constellation) { + default: + v22 |= 4; + case QPSK: + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x1c10046, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x10); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, v20); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v1C); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, v16); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v12); + if (rc < 0) + goto exit_rc; + break; + case QAM_16: + edi |= 0x10; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x1c10046, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x10); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 4); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, v1E); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v1A); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, v14); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v10); + if (rc < 0) + goto exit_rc; + break; + case QAM_64: + edi |= 0x20; + rc = WR16(s, 0x1c10046, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x20); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 8); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, ebx); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v18); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, ebp); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v0E); + if (rc < 0) + goto exit_rc; + break; + } + + if (s->config.s20d24 == 1) { + rc = WR16(s, 0x2010013, 0); + } else { + rc = WR16(s, 0x2010013, 1); + edi |= 0x1000; + } + + switch (fep->u.ofdm.code_rate_HP) { + default: + v22 |= 0x10; + case FEC_1_2: + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 0); + break; + case FEC_2_3: + edi |= 0x200; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 1); + break; + case FEC_3_4: + edi |= 0x400; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 2); + break; + case FEC_5_6: /* 5 */ + edi |= 0x600; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 3); + break; + case FEC_7_8: /* 7 */ + edi |= 0x800; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 4); + break; + }; + if (rc < 0) + goto exit_rc; + + switch (fep->u.ofdm.bandwidth) { + default: + rc = -EINVAL; + goto exit_rc; + case BANDWIDTH_8_MHZ: /* 0 */ + case BANDWIDTH_AUTO: + rc = WR16(s, 0x0c2003f, 0x32); + s->bandwidth_parm = ebx = 0x8b8249; // 9142857 + edx = 0; + break; + case BANDWIDTH_7_MHZ: + rc = WR16(s, 0x0c2003f, 0x3b); + s->bandwidth_parm = ebx = 0x7a1200; // 8000000 + edx = 0x4807; + break; + case BANDWIDTH_6_MHZ: + rc = WR16(s, 0x0c2003f, 0x47); + s->bandwidth_parm = ebx = 0x68a1b6; // 6857142 + edx = 0x0f07; + break; + }; + + if (rc < 0) + goto exit_rc; + + rc = WR16(s, 0x08200ec, edx); + if (rc < 0) + goto exit_rc; + + rc = RD16(s, 0x0820050); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x0820050, rc); + + { + /* Configure bandwidth specific factor */ + ebx = div64_u64(((u64) (s->f_osc) << 21) + (ebx >> 1), + (u64)ebx) - 0x800000; + EXIT_RC(WR16(s, 0x0c50010, ebx & 0xffff)); + EXIT_RC(WR16(s, 0x0c50011, ebx >> 16)); + + /* drx397xD oscillator calibration */ + ebx = div64_u64(((u64) (s->config.f_if + df_tuner) << 28) + + (s->f_osc >> 1), (u64)s->f_osc); + } + ebx &= 0xfffffff; + if (fep->inversion == INVERSION_ON) + ebx = 0x10000000 - ebx; + + EXIT_RC(WR16(s, 0x0c30010, ebx & 0xffff)); + EXIT_RC(WR16(s, 0x0c30011, ebx >> 16)); + + EXIT_RC(WR16(s, 0x0800000, 1)); + EXIT_RC(RD16(s, 0x0800000)); + +#if 0 + rc = WR16(s, 0x0810000, 1); + if (rc < 0) + goto exit_rc; +#endif + + EXIT_RC(SC_WaitForReady(s)); + EXIT_RC(WR16(s, 0x0820042, 0)); + EXIT_RC(WR16(s, 0x0820041, v22)); + EXIT_RC(WR16(s, 0x0820040, edi)); + EXIT_RC(SC_SendCommand(s, 3)); + + rc = RD16(s, 0x0800000); + + SC_WaitForReady(s); + WR16(s, 0x0820042, 0); + WR16(s, 0x0820041, 1); + WR16(s, 0x0820040, 1); + SC_SendCommand(s, 1); + +// rc = WR16(s, 0x2150000, 1); +// if (rc < 0) goto exit_rc; + + rc = WR16(s, 0x2150000, 2); + rc = WR16(s, 0x2150016, a); + rc = WR16(s, 0x2150010, 4); + rc = WR16(s, 0x2150036, 0); + rc = WR16(s, 0x2150000, 1); + s->config.d60 = 2; + exit_rc: + return rc; +} + +/******************************************************************************* + * DVB interface + ******************************************************************************/ + +static int drx397x_init(struct dvb_frontend *fe) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + s->config.rfagc.d00 = 2; /* 0x7c */ + s->config.rfagc.w04 = 0; + s->config.rfagc.w06 = 0x3ff; + + s->config.ifagc.d00 = 0; /* 0x68 */ + s->config.ifagc.w04 = 0; + s->config.ifagc.w06 = 140; + s->config.ifagc.w08 = 0; + s->config.ifagc.w0A = 0x3ff; + s->config.ifagc.w0C = 0x388; + + /* for signal strenght calculations */ + s->config.ss76 = 820; + s->config.ss78 = 2200; + s->config.ss7A = 150; + + /* HI_CfgCommand */ + s->config.w50 = 4; + s->config.w52 = 9; // 0xf; + + s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */ + s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */ + s->config.w92 = 12000; // 20000; + + s->config.w9C = 0x000e; + s->config.w9E = 0x0000; + + /* ConfigureMPEGOutput params */ + s->config.wA0 = 4; + s->config.w98 = 1; // 0; + s->config.w9A = 1; + + /* get chip revision */ + rc = RD16(s, 0x2410019); + if (rc < 0) + return -ENODEV; + + if (rc == 0) { + printk(KERN_INFO "%s: chip revision A2\n", mod_name); + rc = drx_load_fw(s, DRXD_FW_A2); + } else { + + rc = (rc >> 12) - 3; + switch (rc) { + case 1: + s->flags |= F_SET_0D4h; + case 0: + case 4: + s->flags |= F_SET_0D0h; + break; + case 2: + case 5: + break; + case 3: + s->flags |= F_SET_0D4h; + break; + default: + return -ENODEV; + }; + printk(KERN_INFO "%s: chip revision B1.%d\n", mod_name, rc); + rc = drx_load_fw(s, DRXD_FW_B1); + } + if (rc < 0) + goto error; + + rc = WR16(s, 0x0420033, 0x3973); + if (rc < 0) + goto error; + + rc = HI_Command(s, 2); + + msleep(1); + + if (s->chip_rev == DRXD_FW_A2) { + rc = WR16(s, 0x043012d, 0x47F); + if (rc < 0) + goto error; + } + rc = WR16_E0(s, 0x0400000, 0); + if (rc < 0) + goto error; + + if (s->config.w92 > 20000 || s->config.w92 % 4000) { + printk(KERN_ERR "%s: invalid osc frequency\n", mod_name); + rc = -1; + goto error; + } + + rc = WR16(s, 0x2410010, 1); + if (rc < 0) + goto error; + rc = WR16(s, 0x2410011, 0x15); + if (rc < 0) + goto error; + rc = WR16(s, 0x2410012, s->config.w92 / 4000); + if (rc < 0) + goto error; +#ifdef ORIG_FW + rc = WR16(s, 0x2410015, 2); + if (rc < 0) + goto error; +#endif + rc = WR16(s, 0x2410017, 0x3973); + if (rc < 0) + goto error; + + s->f_osc = s->config.f_osc * 1000; /* initial estimator */ +#if 0 + { + int deviation, val, edi; + + /* osc_deviation(struct state *x, int deviation, int io) + * + * io == 0 : read_deviation from eeprom + * io != 0 : write deviation to eeprom + */ + s->config.w64 = x->osc_deviation(x, 0, 0); + val = s->config.w64 * s->config.w66; + deviation = val / 1000000; + + edi = 2; + if (val <= 0) + edi = -2; + val = val % 1000000; + if (edi * val > 1000000) + deviation += edi >> 1; + s->clk_if_corrected += deviation; + } +#endif + + s->config.w56 = 1; + + rc = HI_CfgCommand(s); + if (rc < 0) + goto error; + + rc = write_fw(s, DRXD_InitAtomicRead); + if (rc < 0) + goto error; + + if (s->chip_rev == DRXD_FW_A2) { + rc = WR16(s, 0x2150013, 0); + if (rc < 0) + goto error; + } + + rc = WR16_E0(s, 0x0400002, 0); + if (rc < 0) + goto error; + rc = WR16(s, 0x0400002, 0); + if (rc < 0) + goto error; + + if (s->chip_rev == DRXD_FW_A2) { + rc = write_fw(s, DRXD_ResetCEFR); + if (rc < 0) + goto error; + } + rc = write_fw(s, DRXD_microcode); + if (rc < 0) + goto error; + + s->config.w9C = 0x0e; + if (s->flags & F_SET_0D0h) { + s->config.w9C = 0; + rc = RD16(s, 0x0c20010); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc &= ~0x1000; + rc = WR16(s, 0x0c20010, rc); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc = RD16(s, 0x0c20011); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc &= ~0x8; + rc = WR16(s, 0x0c20011, rc); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc = WR16(s, 0x0c20012, 1); + } + + write_DRXD_InitFE_1: + + rc = write_fw(s, DRXD_InitFE_1); + if (rc < 0) + goto error; + + rc = 1; + if (s->chip_rev == DRXD_FW_B1) { + if (s->flags & F_SET_0D0h) + rc = 0; + } else { + if (s->flags & F_SET_0D0h) + rc = 4; + } + + rc = WR16(s, 0x0C20012, rc); + if (rc < 0) + goto error; + + rc = WR16(s, 0x0C20013, s->config.w9E); + if (rc < 0) + goto error; + rc = WR16(s, 0x0C20015, s->config.w9C); + if (rc < 0) + goto error; + + rc = write_fw(s, DRXD_InitFE_2); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitFT); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitCP); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitCE); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitEQ); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitEC); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitSC); + if (rc < 0) + goto error; + + rc = SetCfgIfAgc(s, &s->config.ifagc); + if (rc < 0) + goto error; + + rc = SetCfgRfAgc(s, &s->config.rfagc); + if (rc < 0) + goto error; + +#if 1 + rc = ConfigureMPEGOutput(s, 1); + rc = WR16(s, 0x08201fe, 0x0017); + rc = WR16(s, 0x08201ff, 0x0101); + + s->config.d5C = 0; +#else + s->config.d5C = 0; + rc = CorrectSysClockDeviation(s); + if (rc < 0) + goto error; + +// rc = ConfigureWR16(s, 0x0420033, 0x200); + + rc = WR16(s, 0x0800000, 0); + if (rc < 0) + goto error; + + rc = WR16(s, 0x2800000, 0); +#endif + s->config.d60 = 1; + s->config.d48 = 1; + error: + return rc; +} + +static int drx397x_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return 0; +} + +static int drx397x_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct drx397xD_state *s = fe->demodulator_priv; + + s->config.s20d24 = 1; // 0; + return drx_tune(s, params); +} + +static int drx397x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 10000; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + return 0; +} + +static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int lockstat; + + GetLockStatus(s, &lockstat); + /* TODO */ +// if (lockstat & 1) +// CorrectSysClockDeviation(s); + + *status = 0; + if (lockstat & 2) { + CorrectSysClockDeviation(s); + ConfigureMPEGOutput(s, 1); + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; + } + if (lockstat & 4) { + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + } + + return 0; +} + +static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber) +{ + *ber = 0; + return 0; +} + +static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + *snr = 0; + return 0; +} + +static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int rc; + + if (s->config.ifagc.d00 == 2) { + *strength = 0xffff; + return 0; + } + rc = RD16(s, 0x0c20035); + if (rc < 0) { + *strength = 0; + return 0; + } + rc &= 0x3ff; + /* Signal strength is calculated using the following formula: + * + * a = 2200 * 150 / (2200 + 150); + * a = a * 3300 / (a + 820); + * b = 2200 * 3300 / (2200 + 820); + * c = (((b-a) * rc) >> 10 + a) << 4; + * strength = ~c & 0xffff; + * + * The following does the same but with less rounding errors: + */ + *strength = ~(7720 + (rc * 30744 >> 10)); + return 0; +} + +static int drx397x_read_ucblocks(struct dvb_frontend *fe, + unsigned int *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int drx397x_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static void drx397x_release(struct dvb_frontend *fe) +{ + struct drx397xD_state *s = fe->demodulator_priv; + printk(KERN_INFO "%s: release demodulator\n", mod_name); + if (s) { + drx_release_fw(s); + kfree(s); + } + +} + +static struct dvb_frontend_ops drx397x_ops = { + + .info = { + .name = "Micronas DRX397xD DVB-T Frontend", + .type = FE_OFDM, + .frequency_min = 47125000, + .frequency_max = 855250000, + .frequency_stepsize = 166667, + .frequency_tolerance = 0, + .caps = /* 0x0C01B2EAE */ + FE_CAN_FEC_1_2 | // = 0x2, + FE_CAN_FEC_2_3 | // = 0x4, + FE_CAN_FEC_3_4 | // = 0x8, + FE_CAN_FEC_5_6 | // = 0x20, + FE_CAN_FEC_7_8 | // = 0x80, + FE_CAN_FEC_AUTO | // = 0x200, + FE_CAN_QPSK | // = 0x400, + FE_CAN_QAM_16 | // = 0x800, + FE_CAN_QAM_64 | // = 0x2000, + FE_CAN_QAM_AUTO | // = 0x10000, + FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000, + FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000, + FE_CAN_HIERARCHY_AUTO | // = 0x100000, + FE_CAN_RECOVER | // = 0x40000000, + FE_CAN_MUTE_TS // = 0x80000000 + }, + + .release = drx397x_release, + .init = drx397x_init, + .sleep = drx397x_sleep, + + .set_frontend = drx397x_set_frontend, + .get_tune_settings = drx397x_get_tune_settings, + .get_frontend = drx397x_get_frontend, + + .read_status = drx397x_read_status, + .read_snr = drx397x_read_snr, + .read_signal_strength = drx397x_read_signal_strength, + .read_ber = drx397x_read_ber, + .read_ucblocks = drx397x_read_ucblocks, +}; + +struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c) +{ + struct drx397xD_state *s = NULL; + + /* allocate memory for the internal state */ + s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL); + if (s == NULL) + goto error; + + /* setup the state */ + s->i2c = i2c; + memcpy(&s->config, config, sizeof(struct drx397xD_config)); + + /* check if the demod is there */ + if (RD16(s, 0x2410019) < 0) + goto error; + + /* create dvb_frontend */ + memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops)); + s->frontend.demodulator_priv = s; + + return &s->frontend; + error: + kfree(s); + return NULL; +} + +MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend"); +MODULE_AUTHOR("Henk Vergonet"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(drx397xD_attach); diff --git a/linux/drivers/media/dvb/frontends/drx397xD.h b/linux/drivers/media/dvb/frontends/drx397xD.h new file mode 100644 index 000000000..ddc7a0797 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/drx397xD.h @@ -0,0 +1,130 @@ +/* + * Driver for Micronas DVB-T drx397xD demodulator + * + * Copyright (C) 2007 Henk vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + */ + +#ifndef _DRX397XD_H_INCLUDED +#define _DRX397XD_H_INCLUDED + +#include <linux/dvb/frontend.h> + +#define DRX_F_STEPSIZE 166667 +#define DRX_F_OFFSET 36000000 + +#define I2C_ADR_C0(x) \ +( (u32)cpu_to_le32( \ + (u32)( \ + (((u32)(x) & (u32)0x000000ffUL) ) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ + (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \ + ( (u32)0x00c00000UL) \ + )) \ +) + +#define I2C_ADR_E0(x) \ +( (u32)cpu_to_le32( \ + (u32)( \ + (((u32)(x) & (u32)0x000000ffUL) ) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ + (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \ + ( (u32)0x00e00000UL) \ + )) \ +) + +struct drx397xD_CfgRfAgc /* 0x7c */ +{ + int d00; /* 2 */ + u16 w04; + u16 w06; +}; + +struct drx397xD_CfgIfAgc /* 0x68 */ +{ + int d00; /* 0 */ + u16 w04; /* 0 */ + u16 w06; + u16 w08; + u16 w0A; + u16 w0C; +}; + +struct drx397xD_s20 { + int d04; + u32 d18; + u32 d1C; + u32 d20; + u32 d14; + u32 d24; + u32 d0C; + u32 d08; +}; + +struct drx397xD_config +{ + /* demodulator's I2C address */ + u8 demod_address; /* 0x0f */ + + struct drx397xD_CfgIfAgc ifagc; /* 0x68 */ + struct drx397xD_CfgRfAgc rfagc; /* 0x7c */ + u32 s20d24; + + /* HI_CfgCommand parameters */ + u16 w50, w52, /* w54, */ w56; + + int d5C; + int d60; + int d48; + int d28; + + u32 f_if; /* d14: intermediate frequency [Hz] */ + /* 36000000 on Cinergy 2400i DT */ + /* 42800000 on Pinnacle Hybrid PRO 330e */ + + u16 f_osc; /* s66: 48000 oscillator frequency [kHz] */ + + u16 w92; /* 20000 */ + + u16 wA0; + u16 w98; + u16 w9A; + + u16 w9C; /* 0xe0 */ + u16 w9E; /* 0x00 */ + + /* used for signal strength calculations in + drx397x_read_signal_strength + */ + u16 ss78; // 2200 + u16 ss7A; // 150 + u16 ss76; // 820 +}; + +#if defined(CONFIG_DVB_DRX397XD) || (defined(CONFIG_DVB_DRX397XD_MODULE) && defined(MODULE)) +extern struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_DRX397XD */ + +#endif /* _DRX397XD_H_INCLUDED */ diff --git a/linux/drivers/media/dvb/frontends/drx397xD_fw.h b/linux/drivers/media/dvb/frontends/drx397xD_fw.h new file mode 100644 index 000000000..01de02a81 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/drx397xD_fw.h @@ -0,0 +1,40 @@ +/* + * Firmware definitions for Micronas drx397xD + * + * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef _FW_ENTRY + _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ), + _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ), +#undef _FW_ENTRY +#endif /* _FW_ENTRY */ + +#ifdef _BLOB_ENTRY + _BLOB_ENTRY("InitAtomicRead", DRXD_InitAtomicRead = 0 ), + _BLOB_ENTRY("InitCE", DRXD_InitCE ), + _BLOB_ENTRY("InitCP", DRXD_InitCP ), + _BLOB_ENTRY("InitEC", DRXD_InitEC ), + _BLOB_ENTRY("InitEQ", DRXD_InitEQ ), + _BLOB_ENTRY("InitFE_1", DRXD_InitFE_1 ), + _BLOB_ENTRY("InitFE_2", DRXD_InitFE_2 ), + _BLOB_ENTRY("InitFT", DRXD_InitFT ), + _BLOB_ENTRY("InitSC", DRXD_InitSC ), + _BLOB_ENTRY("ResetCEFR", DRXD_ResetCEFR ), + _BLOB_ENTRY("ResetECRAM", DRXD_ResetECRAM ), + _BLOB_ENTRY("microcode", DRXD_microcode ), +#undef _BLOB_ENTRY +#endif /* _BLOB_ENTRY */ diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 93785eef2..bb3e35373 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -344,6 +344,52 @@ static struct dvb_pll_desc dvb_pll_opera1 = { } }; +static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf, + const struct dvb_frontend_parameters *params) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { + .addr = priv->pll_i2c_address, + .flags = 0, + .buf = buf, + .len = 4 + }; + int result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + printk(KERN_ERR "%s: i2c_transfer failed:%d", + __func__, result); + + buf[2] = 0x9e; + buf[3] = 0x90; + + return; +} + +/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ +static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { + .name = "Samsung DTOS403IH102A", + .min = 44250000, + .max = 858000000, + .iffreq = 36125000, + .count = 8, + .set = samsung_dtos403ih102a_set, + .entries = { + { 135000000, 62500, 0xbe, 0x01 }, + { 177000000, 62500, 0xf6, 0x01 }, + { 370000000, 62500, 0xbe, 0x02 }, + { 450000000, 62500, 0xf6, 0x02 }, + { 466000000, 62500, 0xfe, 0x02 }, + { 538000000, 62500, 0xbe, 0x08 }, + { 826000000, 62500, 0xf6, 0x08 }, + { 999999999, 62500, 0xfe, 0x08 }, + } +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -361,6 +407,7 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, [DVB_PLL_OPERA1] = &dvb_pll_opera1, + [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, }; /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index 872ca29e7..05239f579 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -22,6 +22,7 @@ #define DVB_PLL_SAMSUNG_TBMV 11 #define DVB_PLL_PHILIPS_SD1878_TDA8261 12 #define DVB_PLL_OPERA1 13 +#define DVB_PLL_SAMSUNG_DTOS403IH102A 14 /** * Attach a dvb-pll to the supplied frontend structure. diff --git a/linux/drivers/media/dvb/frontends/itd1000.c b/linux/drivers/media/dvb/frontends/itd1000.c index abcd0ddae..3e37beb14 100644 --- a/linux/drivers/media/dvb/frontends/itd1000.c +++ b/linux/drivers/media/dvb/frontends/itd1000.c @@ -203,7 +203,7 @@ static void itd1000_set_vco(struct itd1000_state *state, u32 freq_khz) } } -struct { +static const struct { u32 freq; u8 values[10]; /* RFTR, RFST1 - RFST9 */ } itd1000_fre_values[] = { diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c index 081ca3398..5ac9b1592 100644 --- a/linux/drivers/media/dvb/frontends/mt312.c +++ b/linux/drivers/media/dvb/frontends/mt312.c @@ -737,7 +737,7 @@ static void mt312_release(struct dvb_frontend *fe) } #define MT312_SYS_CLK 90000000UL /* 90 MHz */ -static struct dvb_frontend_ops vp310_mt312_ops = { +static struct dvb_frontend_ops mt312_ops = { .info = { .name = "Zarlink ???? DVB-S", @@ -776,7 +776,7 @@ static struct dvb_frontend_ops vp310_mt312_ops = { .set_voltage = mt312_set_voltage, }; -struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config, +struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct i2c_adapter *i2c) { struct mt312_state *state = NULL; @@ -795,7 +795,7 @@ struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config, goto error; /* create dvb_frontend */ - memcpy(&state->frontend.ops, &vp310_mt312_ops, + memcpy(&state->frontend.ops, &mt312_ops, sizeof(struct dvb_frontend_ops)); state->frontend.demodulator_priv = state; @@ -827,12 +827,13 @@ error: kfree(state); return NULL; } -EXPORT_SYMBOL(vp310_mt312_attach); +EXPORT_SYMBOL(mt312_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("Zarlink VP310/MT312/ZL10313 DVB-S Demodulator driver"); MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>"); +MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/mt312.h b/linux/drivers/media/dvb/frontends/mt312.h index 96338f0c4..29e3bb549 100644 --- a/linux/drivers/media/dvb/frontends/mt312.h +++ b/linux/drivers/media/dvb/frontends/mt312.h @@ -33,14 +33,14 @@ struct mt312_config { u8 demod_address; /* inverted voltage setting */ - int voltage_inverted:1; + unsigned int voltage_inverted:1; }; #if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE)) -struct dvb_frontend *vp310_mt312_attach(const struct mt312_config *config, +struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct i2c_adapter *i2c); #else -static inline struct dvb_frontend *vp310_mt312_attach( +static inline struct dvb_frontend *mt312_attach( const struct mt312_config *config, struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index 8a64cb43e..c7b5785f8 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -91,7 +91,7 @@ static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) Less code and more efficient that loading a buffer on the stack with the bytes to send and then calling or51132_writebuf() on that. */ #define or51132_writebytes(state, data...) \ - ({ const static u8 _data[] = {data}; \ + ({ static const u8 _data[] = {data}; \ or51132_writebuf(state, _data, sizeof(_data)); }) /* Read data from demod into buffer. Returns 0 on success. */ @@ -132,7 +132,7 @@ static int or51132_readreg(struct or51132_state *state, u8 reg) static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) { struct or51132_state* state = fe->demodulator_priv; - const static u8 run_buf[] = {0x7F,0x01}; + static const u8 run_buf[] = {0x7F,0x01}; u8 rec_buf[8]; u32 firmwareAsize, firmwareBsize; int i,ret; diff --git a/linux/drivers/media/dvb/frontends/s5h1409.c b/linux/drivers/media/dvb/frontends/s5h1409.c index b999ec424..5ddb2dca3 100644 --- a/linux/drivers/media/dvb/frontends/s5h1409.c +++ b/linux/drivers/media/dvb/frontends/s5h1409.c @@ -26,7 +26,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include "dvb_frontend.h" -#include "dvb-pll.h" #include "s5h1409.h" struct s5h1409_state { diff --git a/linux/drivers/media/dvb/frontends/s5h1411.c b/linux/drivers/media/dvb/frontends/s5h1411.c new file mode 100644 index 000000000..cff360ce1 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/s5h1411.c @@ -0,0 +1,887 @@ +/* + Samsung S5H1411 VSB/QAM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "s5h1411.h" + +struct s5h1411_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct s5h1411_config *config; + + struct dvb_frontend frontend; + + fe_modulation_t current_modulation; + + u32 current_frequency; + int if_freq; + + u8 inversion; +}; + +static int debug; + +#define dprintk(arg...) do { \ + if (debug) \ + printk(arg); \ + } while (0) + +/* Register values to initialise the demod, defaults to VSB */ +static struct init_tab { + u8 addr; + u8 reg; + u16 data; +} init_tab[] = { + { S5H1411_I2C_TOP_ADDR, 0x00, 0x0071, }, + { S5H1411_I2C_TOP_ADDR, 0x08, 0x0047, }, + { S5H1411_I2C_TOP_ADDR, 0x1c, 0x0400, }, + { S5H1411_I2C_TOP_ADDR, 0x1e, 0x0370, }, + { S5H1411_I2C_TOP_ADDR, 0x1f, 0x342a, }, + { S5H1411_I2C_TOP_ADDR, 0x24, 0x0231, }, + { S5H1411_I2C_TOP_ADDR, 0x25, 0x1011, }, + { S5H1411_I2C_TOP_ADDR, 0x26, 0x0f07, }, + { S5H1411_I2C_TOP_ADDR, 0x27, 0x0f04, }, + { S5H1411_I2C_TOP_ADDR, 0x28, 0x070f, }, + { S5H1411_I2C_TOP_ADDR, 0x29, 0x2820, }, + { S5H1411_I2C_TOP_ADDR, 0x2a, 0x102e, }, + { S5H1411_I2C_TOP_ADDR, 0x2b, 0x0220, }, + { S5H1411_I2C_TOP_ADDR, 0x2e, 0x0d0e, }, + { S5H1411_I2C_TOP_ADDR, 0x2f, 0x1013, }, + { S5H1411_I2C_TOP_ADDR, 0x31, 0x171b, }, + { S5H1411_I2C_TOP_ADDR, 0x32, 0x0e0f, }, + { S5H1411_I2C_TOP_ADDR, 0x33, 0x0f10, }, + { S5H1411_I2C_TOP_ADDR, 0x34, 0x170e, }, + { S5H1411_I2C_TOP_ADDR, 0x35, 0x4b10, }, + { S5H1411_I2C_TOP_ADDR, 0x36, 0x0f17, }, + { S5H1411_I2C_TOP_ADDR, 0x3c, 0x1577, }, + { S5H1411_I2C_TOP_ADDR, 0x3d, 0x081a, }, + { S5H1411_I2C_TOP_ADDR, 0x3e, 0x77ee, }, + { S5H1411_I2C_TOP_ADDR, 0x40, 0x1e09, }, + { S5H1411_I2C_TOP_ADDR, 0x41, 0x0f0c, }, + { S5H1411_I2C_TOP_ADDR, 0x42, 0x1f10, }, + { S5H1411_I2C_TOP_ADDR, 0x4d, 0x0509, }, + { S5H1411_I2C_TOP_ADDR, 0x4e, 0x0a00, }, + { S5H1411_I2C_TOP_ADDR, 0x50, 0x0000, }, + { S5H1411_I2C_TOP_ADDR, 0x5b, 0x0000, }, + { S5H1411_I2C_TOP_ADDR, 0x5c, 0x0008, }, + { S5H1411_I2C_TOP_ADDR, 0x57, 0x1101, }, + { S5H1411_I2C_TOP_ADDR, 0x65, 0x007c, }, + { S5H1411_I2C_TOP_ADDR, 0x68, 0x0512, }, + { S5H1411_I2C_TOP_ADDR, 0x69, 0x0258, }, + { S5H1411_I2C_TOP_ADDR, 0x70, 0x0004, }, + { S5H1411_I2C_TOP_ADDR, 0x71, 0x0007, }, + { S5H1411_I2C_TOP_ADDR, 0x76, 0x00a9, }, + { S5H1411_I2C_TOP_ADDR, 0x78, 0x3141, }, + { S5H1411_I2C_TOP_ADDR, 0x7a, 0x3141, }, + { S5H1411_I2C_TOP_ADDR, 0xb3, 0x8003, }, + { S5H1411_I2C_TOP_ADDR, 0xb5, 0xafbb, }, + { S5H1411_I2C_TOP_ADDR, 0xb5, 0xa6bb, }, + { S5H1411_I2C_TOP_ADDR, 0xb6, 0x0609, }, + { S5H1411_I2C_TOP_ADDR, 0xb7, 0x2f06, }, + { S5H1411_I2C_TOP_ADDR, 0xb8, 0x003f, }, + { S5H1411_I2C_TOP_ADDR, 0xb9, 0x2700, }, + { S5H1411_I2C_TOP_ADDR, 0xba, 0xfac8, }, + { S5H1411_I2C_TOP_ADDR, 0xbe, 0x1003, }, + { S5H1411_I2C_TOP_ADDR, 0xbf, 0x103f, }, + { S5H1411_I2C_TOP_ADDR, 0xce, 0x2000, }, + { S5H1411_I2C_TOP_ADDR, 0xcf, 0x0800, }, + { S5H1411_I2C_TOP_ADDR, 0xd0, 0x0800, }, + { S5H1411_I2C_TOP_ADDR, 0xd1, 0x0400, }, + { S5H1411_I2C_TOP_ADDR, 0xd2, 0x0800, }, + { S5H1411_I2C_TOP_ADDR, 0xd3, 0x2000, }, + { S5H1411_I2C_TOP_ADDR, 0xd4, 0x3000, }, + { S5H1411_I2C_TOP_ADDR, 0xdb, 0x4a9b, }, + { S5H1411_I2C_TOP_ADDR, 0xdc, 0x1000, }, + { S5H1411_I2C_TOP_ADDR, 0xde, 0x0001, }, + { S5H1411_I2C_TOP_ADDR, 0xdf, 0x0000, }, + { S5H1411_I2C_TOP_ADDR, 0xe3, 0x0301, }, + { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0000, }, + { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0001, }, + { S5H1411_I2C_QAM_ADDR, 0x08, 0x0600, }, + { S5H1411_I2C_QAM_ADDR, 0x18, 0x4201, }, + { S5H1411_I2C_QAM_ADDR, 0x1e, 0x6476, }, + { S5H1411_I2C_QAM_ADDR, 0x21, 0x0830, }, + { S5H1411_I2C_QAM_ADDR, 0x0c, 0x5679, }, + { S5H1411_I2C_QAM_ADDR, 0x0d, 0x579b, }, + { S5H1411_I2C_QAM_ADDR, 0x24, 0x0102, }, + { S5H1411_I2C_QAM_ADDR, 0x31, 0x7488, }, + { S5H1411_I2C_QAM_ADDR, 0x32, 0x0a08, }, + { S5H1411_I2C_QAM_ADDR, 0x3d, 0x8689, }, + { S5H1411_I2C_QAM_ADDR, 0x49, 0x0048, }, + { S5H1411_I2C_QAM_ADDR, 0x57, 0x2012, }, + { S5H1411_I2C_QAM_ADDR, 0x5d, 0x7676, }, + { S5H1411_I2C_QAM_ADDR, 0x04, 0x0400, }, + { S5H1411_I2C_QAM_ADDR, 0x58, 0x00c0, }, + { S5H1411_I2C_QAM_ADDR, 0x5b, 0x0100, }, +}; + +/* VSB SNR lookup table */ +static struct vsb_snr_tab { + u16 val; + u16 data; +} vsb_snr_tab[] = { + { 0x39f, 300, }, + { 0x39b, 295, }, + { 0x397, 290, }, + { 0x394, 285, }, + { 0x38f, 280, }, + { 0x38b, 275, }, + { 0x387, 270, }, + { 0x382, 265, }, + { 0x37d, 260, }, + { 0x377, 255, }, + { 0x370, 250, }, + { 0x36a, 245, }, + { 0x364, 240, }, + { 0x35b, 235, }, + { 0x353, 230, }, + { 0x349, 225, }, + { 0x340, 320, }, + { 0x337, 215, }, + { 0x327, 210, }, + { 0x31b, 205, }, + { 0x310, 200, }, + { 0x302, 195, }, + { 0x2f3, 190, }, + { 0x2e4, 185, }, + { 0x2d7, 180, }, + { 0x2cd, 175, }, + { 0x2bb, 170, }, + { 0x2a9, 165, }, + { 0x29e, 160, }, + { 0x284, 155, }, + { 0x27a, 150, }, + { 0x260, 145, }, + { 0x23a, 140, }, + { 0x224, 135, }, + { 0x213, 130, }, + { 0x204, 125, }, + { 0x1fe, 120, }, + { 0, 0, }, +}; + +/* QAM64 SNR lookup table */ +static struct qam64_snr_tab { + u16 val; + u16 data; +} qam64_snr_tab[] = { + { 0x0001, 0, }, + { 0x0af0, 300, }, + { 0x0d80, 290, }, + { 0x10a0, 280, }, + { 0x14b5, 270, }, + { 0x1590, 268, }, + { 0x1680, 266, }, + { 0x17b0, 264, }, + { 0x18c0, 262, }, + { 0x19b0, 260, }, + { 0x1ad0, 258, }, + { 0x1d00, 256, }, + { 0x1da0, 254, }, + { 0x1ef0, 252, }, + { 0x2050, 250, }, + { 0x20f0, 249, }, + { 0x21d0, 248, }, + { 0x22b0, 247, }, + { 0x23a0, 246, }, + { 0x2470, 245, }, + { 0x24f0, 244, }, + { 0x25a0, 243, }, + { 0x26c0, 242, }, + { 0x27b0, 241, }, + { 0x28d0, 240, }, + { 0x29b0, 239, }, + { 0x2ad0, 238, }, + { 0x2ba0, 237, }, + { 0x2c80, 236, }, + { 0x2d20, 235, }, + { 0x2e00, 234, }, + { 0x2f10, 233, }, + { 0x3050, 232, }, + { 0x3190, 231, }, + { 0x3300, 230, }, + { 0x3340, 229, }, + { 0x3200, 228, }, + { 0x3550, 227, }, + { 0x3610, 226, }, + { 0x3600, 225, }, + { 0x3700, 224, }, + { 0x3800, 223, }, + { 0x3920, 222, }, + { 0x3a20, 221, }, + { 0x3b30, 220, }, + { 0x3d00, 219, }, + { 0x3e00, 218, }, + { 0x4000, 217, }, + { 0x4100, 216, }, + { 0x4300, 215, }, + { 0x4400, 214, }, + { 0x4600, 213, }, + { 0x4700, 212, }, + { 0x4800, 211, }, + { 0x4a00, 210, }, + { 0x4b00, 209, }, + { 0x4d00, 208, }, + { 0x4f00, 207, }, + { 0x5050, 206, }, + { 0x5200, 205, }, + { 0x53c0, 204, }, + { 0x5450, 203, }, + { 0x5650, 202, }, + { 0x5820, 201, }, + { 0x6000, 200, }, + { 0xffff, 0, }, +}; + +/* QAM256 SNR lookup table */ +static struct qam256_snr_tab { + u16 val; + u16 data; +} qam256_snr_tab[] = { + { 0x0001, 0, }, + { 0x0970, 400, }, + { 0x0a90, 390, }, + { 0x0b90, 380, }, + { 0x0d90, 370, }, + { 0x0ff0, 360, }, + { 0x1240, 350, }, + { 0x1345, 348, }, + { 0x13c0, 346, }, + { 0x14c0, 344, }, + { 0x1500, 342, }, + { 0x1610, 340, }, + { 0x1700, 338, }, + { 0x1800, 336, }, + { 0x18b0, 334, }, + { 0x1900, 332, }, + { 0x1ab0, 330, }, + { 0x1bc0, 328, }, + { 0x1cb0, 326, }, + { 0x1db0, 324, }, + { 0x1eb0, 322, }, + { 0x2030, 320, }, + { 0x2200, 318, }, + { 0x2280, 316, }, + { 0x2410, 314, }, + { 0x25b0, 312, }, + { 0x27a0, 310, }, + { 0x2840, 308, }, + { 0x29d0, 306, }, + { 0x2b10, 304, }, + { 0x2d30, 302, }, + { 0x2f20, 300, }, + { 0x30c0, 298, }, + { 0x3260, 297, }, + { 0x32c0, 296, }, + { 0x3300, 295, }, + { 0x33b0, 294, }, + { 0x34b0, 293, }, + { 0x35a0, 292, }, + { 0x3650, 291, }, + { 0x3800, 290, }, + { 0x3900, 289, }, + { 0x3a50, 288, }, + { 0x3b30, 287, }, + { 0x3cb0, 286, }, + { 0x3e20, 285, }, + { 0x3fa0, 284, }, + { 0x40a0, 283, }, + { 0x41c0, 282, }, + { 0x42f0, 281, }, + { 0x44a0, 280, }, + { 0x4600, 279, }, + { 0x47b0, 278, }, + { 0x4900, 277, }, + { 0x4a00, 276, }, + { 0x4ba0, 275, }, + { 0x4d00, 274, }, + { 0x4f00, 273, }, + { 0x5000, 272, }, + { 0x51f0, 272, }, + { 0x53a0, 270, }, + { 0x5520, 269, }, + { 0x5700, 268, }, + { 0x5800, 267, }, + { 0x5a00, 266, }, + { 0x5c00, 265, }, + { 0x5d00, 264, }, + { 0x5f00, 263, }, + { 0x6000, 262, }, + { 0x6200, 261, }, + { 0x6400, 260, }, + { 0xffff, 0, }, +}; + +/* 8 bit registers, 16 bit values */ +static int s5h1411_writereg(struct s5h1411_state *state, + u8 addr, u8 reg, u16 data) +{ + int ret; + u8 buf [] = { reg, data >> 8, data & 0xff }; + + struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, " + "ret == %i)\n", __func__, addr, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static u16 s5h1411_readreg(struct s5h1411_state *state, u8 addr, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0, 0 }; + + struct i2c_msg msg [] = { + { .addr = addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + return (b1[0] << 8) | b1[1]; +} + +static int s5h1411_softreset(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 0); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 1); + return 0; +} + +static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(%d KHz)\n", __func__, KHz); + + switch (KHz) { + case 3250: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x10d9); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x5342); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x10d9); + break; + case 3500: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1225); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x1e96); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1225); + break; + case 4000: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x14bc); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0xb53e); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x14bd); + break; + default: + dprintk("%s(%d KHz) Invalid, defaulting to 5380\n", + __func__, KHz); + /* no break, need to continue */ + case 5380: + case 44000: + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x3655); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1be4); + break; + } + + state->if_freq = KHz; + + return 0; +} + +static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, mode); + + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff; + switch (mode) { + case S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: + val |= 0x0000; + break; + case S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: + dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); + val |= 0x1000; + break; + case S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: + val |= 0x2000; + break; + case S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: + val |= 0x3000; + break; + default: + return -EINVAL; + } + + /* Configure MPEG Signal Timing charactistics */ + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbe, val); +} + +static int s5h1411_set_spectralinversion(struct dvb_frontend *fe, int inversion) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, inversion); + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x24) & ~0x1000; + + if (inversion == 1) + val |= 0x1000; /* Inverted */ + else + val |= 0x0000; + + state->inversion = inversion; + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x24, val); +} + +static int s5h1411_enable_modulation(struct dvb_frontend *fe, + fe_modulation_t m) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(0x%08x)\n", __func__, m); + + switch (m) { + case VSB_8: + dprintk("%s() VSB_8\n", __func__); + s5h1411_set_if_freq(fe, state->config->vsb_if); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x71); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x00); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0xf1); + break; + case QAM_64: + case QAM_256: + dprintk("%s() QAM_AUTO (64/256)\n", __func__); + s5h1411_set_if_freq(fe, state->config->qam_if); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x0171); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x0001); + s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x16, 0x1101); + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0x00f0); + break; + default: + dprintk("%s() Invalid modulation\n", __func__); + return -EINVAL; + } + + state->current_modulation = m; + s5h1411_softreset(fe); + + return 0; +} + +static int s5h1411_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); + else + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 0); +} + +static int s5h1411_set_gpio(struct dvb_frontend *fe, int enable) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 val; + + dprintk("%s(%d)\n", __func__, enable); + + val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xe0) & ~0x02; + + if (enable) + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, + val | 0x02); + else + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, val); +} + +static int s5h1411_sleep(struct dvb_frontend *fe, int enable) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (enable) + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 1); + else { + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 0); + s5h1411_softreset(fe); + } + + return 0; +} + +static int s5h1411_register_reset(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __func__); + + return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf3, 0); +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +static int s5h1411_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + dprintk("%s(frequency=%d)\n", __func__, p->frequency); + + s5h1411_softreset(fe); + + state->current_frequency = p->frequency; + + s5h1411_enable_modulation(fe, p->u.vsb.modulation); + + /* Allow the demod to settle */ + msleep(100); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.set_params(fe, p); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int s5h1411_init(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + int i; + + dprintk("%s()\n", __func__); + + s5h1411_sleep(fe, 0); + s5h1411_register_reset(fe); + + for (i = 0; i < ARRAY_SIZE(init_tab); i++) + s5h1411_writereg(state, init_tab[i].addr, + init_tab[i].reg, + init_tab[i].data); + + /* The datasheet says that after initialisation, VSB is default */ + state->current_modulation = VSB_8; + + if (state->config->output_mode == S5H1411_SERIAL_OUTPUT) + /* Serial */ + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbd, 0x1101); + else + /* Parallel */ + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbd, 0x1001); + + s5h1411_set_spectralinversion(fe, state->config->inversion); + s5h1411_set_if_freq(fe, state->config->vsb_if); + s5h1411_set_gpio(fe, state->config->gpio); + s5h1411_set_mpeg_timing(fe, state->config->mpeg_timing); + s5h1411_softreset(fe); + + /* Note: Leaving the I2C gate closed. */ + s5h1411_i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int s5h1411_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 reg; + u32 tuner_status = 0; + + *status = 0; + + /* Get the demodulator status */ + reg = (s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf2) >> 15) + & 0x0001; + if (reg) + *status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_SIGNAL; + + switch (state->current_modulation) { + case QAM_64: + case QAM_256: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf0); + if (reg & 0x100) + *status |= FE_HAS_VITERBI; + if (reg & 0x10) + *status |= FE_HAS_SYNC; + break; + case VSB_8: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x5e); + if (reg & 0x0001) + *status |= FE_HAS_SYNC; + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf2); + if (reg & 0x1000) + *status |= FE_HAS_VITERBI; + break; + default: + return -EINVAL; + } + + switch (state->config->status_mode) { + case S5H1411_DEMODLOCKING: + if (*status & FE_HAS_VITERBI) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + case S5H1411_TUNERLOCKING: + /* Get the tuner status */ + if (fe->ops.tuner_ops.get_status) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + if (tuner_status) + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + break; + } + + dprintk("%s() status 0x%08x\n", __func__, *status); + + return 0; +} + +static int s5h1411_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { + if (v < qam256_snr_tab[i].val) { + *snr = qam256_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1411_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { + if (v < qam64_snr_tab[i].val) { + *snr = qam64_snr_tab[i].data; + ret = 0; + break; + } + } + return ret; +} + +static int s5h1411_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +{ + int i, ret = -EINVAL; + dprintk("%s()\n", __func__); + + for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { + if (v > vsb_snr_tab[i].val) { + *snr = vsb_snr_tab[i].data; + ret = 0; + break; + } + } + dprintk("%s() snr=%d\n", __func__, *snr); + return ret; +} + +static int s5h1411_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct s5h1411_state *state = fe->demodulator_priv; + u16 reg; + dprintk("%s()\n", __func__); + + switch (state->current_modulation) { + case QAM_64: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); + return s5h1411_qam64_lookup_snr(fe, snr, reg); + case QAM_256: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); + return s5h1411_qam256_lookup_snr(fe, snr, reg); + case VSB_8: + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, + 0xf2) & 0x3ff; + return s5h1411_vsb_lookup_snr(fe, snr, reg); + default: + break; + } + + return -EINVAL; +} + +static int s5h1411_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + return s5h1411_read_snr(fe, signal_strength); +} + +static int s5h1411_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + *ucblocks = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xc9); + + return 0; +} + +static int s5h1411_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + return s5h1411_read_ucblocks(fe, ber); +} + +static int s5h1411_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct s5h1411_state *state = fe->demodulator_priv; + + p->frequency = state->current_frequency; + p->u.vsb.modulation = state->current_modulation; + + return 0; +} + +static int s5h1411_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void s5h1411_release(struct dvb_frontend *fe) +{ + struct s5h1411_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops s5h1411_ops; + +struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, + struct i2c_adapter *i2c) +{ + struct s5h1411_state *state = NULL; + u16 reg; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->current_modulation = VSB_8; + state->inversion = state->config->inversion; + + /* check if the demod exists */ + reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x05); + if (reg != 0x0066) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &s5h1411_ops, + sizeof(struct dvb_frontend_ops)); + + state->frontend.demodulator_priv = state; + + if (s5h1411_init(&state->frontend) != 0) { + printk(KERN_ERR "%s: Failed to initialize correctly\n", + __func__); + goto error; + } + + /* Note: Leaving the I2C gate open here. */ + s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(s5h1411_attach); + +static struct dvb_frontend_ops s5h1411_ops = { + + .info = { + .name = "Samsung S5H1411 QAM/8VSB Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + + .init = s5h1411_init, + .i2c_gate_ctrl = s5h1411_i2c_gate_ctrl, + .set_frontend = s5h1411_set_frontend, + .get_frontend = s5h1411_get_frontend, + .get_tune_settings = s5h1411_get_tune_settings, + .read_status = s5h1411_read_status, + .read_ber = s5h1411_read_ber, + .read_signal_strength = s5h1411_read_signal_strength, + .read_snr = s5h1411_read_snr, + .read_ucblocks = s5h1411_read_ucblocks, + .release = s5h1411_release, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + */ diff --git a/linux/drivers/media/dvb/frontends/s5h1411.h b/linux/drivers/media/dvb/frontends/s5h1411.h new file mode 100644 index 000000000..1855f64ed --- /dev/null +++ b/linux/drivers/media/dvb/frontends/s5h1411.h @@ -0,0 +1,90 @@ +/* + Samsung S5H1411 VSB/QAM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __S5H1411_H__ +#define __S5H1411_H__ + +#include <linux/dvb/frontend.h> + +#define S5H1411_I2C_TOP_ADDR (0x32 >> 1) +#define S5H1411_I2C_QAM_ADDR (0x34 >> 1) + +struct s5h1411_config { + + /* serial/parallel output */ +#define S5H1411_PARALLEL_OUTPUT 0 +#define S5H1411_SERIAL_OUTPUT 1 + u8 output_mode; + + /* GPIO Setting */ +#define S5H1411_GPIO_OFF 0 +#define S5H1411_GPIO_ON 1 + u8 gpio; + + /* MPEG signal timing */ +#define S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 +#define S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 +#define S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 +#define S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 + u16 mpeg_timing; + + /* IF Freq for QAM and VSB in KHz */ +#define S5H1411_IF_2500 2500 +#define S5H1411_IF_3500 3500 +#define S5H1411_IF_4000 4000 +#define S5H1411_IF_5380 5380 +#define S5H1411_IF_44000 44000 +#define S5H1411_VSB_IF_DEFAULT S5H1411_IF_44000 +#define S5H1411_QAM_IF_DEFAULT S5H1411_IF_44000 + u16 qam_if; + u16 vsb_if; + + /* Spectral Inversion */ +#define S5H1411_INVERSION_OFF 0 +#define S5H1411_INVERSION_ON 1 + u8 inversion; + + /* Return lock status based on tuner lock, or demod lock */ +#define S5H1411_TUNERLOCKING 0 +#define S5H1411_DEMODLOCKING 1 + u8 status_mode; +}; + +#if defined(CONFIG_DVB_S5H1411) || \ + (defined(CONFIG_DVB_S5H1411_MODULE) && defined(MODULE)) +extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *s5h1411_attach( + const struct s5h1411_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_S5H1411 */ + +#endif /* __S5H1411_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + */ diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index 4f4f8f918..996f37704 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -485,7 +485,7 @@ static void s5h1420_setsymbolrate(struct s5h1420_state* state, val *= 2; do_div(val, (state->fclk / 1000)); - dprintk("symbol rate register: %06llx\n", val); + dprintk("symbol rate register: %06llx\n", (unsigned long long)val); v = s5h1420_readreg(state, Loop01); s5h1420_writereg(state, Loop01, v & 0x7f); diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c index f7c367415..17556183e 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.c +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -366,26 +366,32 @@ static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltag * H/V switching over OP0, OP1 and OP2 are LNB power enable bits */ reg0x0c &= 0x0f; - - if (voltage == SEC_VOLTAGE_OFF) { - stv0299_writeregI (state, 0x0c, 0x00); /* LNB power off! */ - return stv0299_writeregI (state, 0x08, 0x00); /* LNB power off! */ - } - - stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6)); + reg0x08 = (reg0x08 & 0x3f) | (state->config->lock_output << 6); switch (voltage) { case SEC_VOLTAGE_13: - if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) reg0x0c |= 0x10; - else reg0x0c |= 0x40; - - return stv0299_writeregI(state, 0x0c, reg0x0c); - + if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) + reg0x0c |= 0x10; /* OP1 off, OP0 on */ + else + reg0x0c |= 0x40; /* OP1 on, OP0 off */ + break; case SEC_VOLTAGE_18: - return stv0299_writeregI(state, 0x0c, reg0x0c | 0x50); + reg0x0c |= 0x50; /* OP1 on, OP0 on */ + break; + case SEC_VOLTAGE_OFF: + /* LNB power off! */ + reg0x08 = 0x00; + reg0x0c = 0x00; + break; default: return -EINVAL; }; + + if (state->config->op0_off) + reg0x0c &= ~0x10; + + stv0299_writeregI(state, 0x08, reg0x08); + return stv0299_writeregI(state, 0x0c, reg0x0c); } static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long cmd) @@ -445,11 +451,20 @@ static int stv0299_init (struct dvb_frontend* fe) { struct stv0299_state* state = fe->demodulator_priv; int i; + u8 reg; + u8 val; dprintk("stv0299: init chip\n"); - for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) - stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]); + for (i = 0; ; i += 2) { + reg = state->config->inittab[i]; + val = state->config->inittab[i+1]; + if (reg == 0xff && val == 0xff) + break; + if (reg == 0x0c && state->config->op0_off) + val &= ~0x10; + stv0299_writeregI(state, reg, val); + } return 0; } diff --git a/linux/drivers/media/dvb/frontends/stv0299.h b/linux/drivers/media/dvb/frontends/stv0299.h index 84eaeb518..3282f4302 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.h +++ b/linux/drivers/media/dvb/frontends/stv0299.h @@ -48,10 +48,10 @@ #include <linux/dvb/frontend.h> #include "dvb_frontend.h" -#define STV0229_LOCKOUTPUT_0 0 -#define STV0229_LOCKOUTPUT_1 1 -#define STV0229_LOCKOUTPUT_CF 2 -#define STV0229_LOCKOUTPUT_LK 3 +#define STV0299_LOCKOUTPUT_0 0 +#define STV0299_LOCKOUTPUT_1 1 +#define STV0299_LOCKOUTPUT_CF 2 +#define STV0299_LOCKOUTPUT_LK 3 #define STV0299_VOLT13_OP0 0 #define STV0299_VOLT13_OP1 1 @@ -82,6 +82,9 @@ struct stv0299_config /* Is 13v controlled by OP0 or OP1? */ u8 volt13_op0_op1:1; + /* Turn-off OP0? */ + u8 op0_off:1; + /* minimum delay before retuning */ int min_delay_ms; diff --git a/linux/drivers/media/dvb/frontends/tda10023.c b/linux/drivers/media/dvb/frontends/tda10023.c index 3195a80a9..82d6cb5b8 100644 --- a/linux/drivers/media/dvb/frontends/tda10023.c +++ b/linux/drivers/media/dvb/frontends/tda10023.c @@ -38,17 +38,24 @@ #include "dvb_frontend.h" #include "tda1002x.h" +#define REG0_INIT_VAL 0x23 struct tda10023_state { struct i2c_adapter* i2c; /* configuration settings */ - const struct tda1002x_config* config; + const struct tda10023_config *config; struct dvb_frontend frontend; u8 pwm; u8 reg0; -}; + /* clock settings */ + u32 xtal; + u8 pll_m; + u8 pll_p; + u8 pll_n; + u32 sysclk; +}; #if 0 #define dprintk(x...) printk(x) @@ -58,59 +65,6 @@ struct tda10023_state { static int verbose; -#define XTAL 28920000UL -#define PLL_M 8UL -#define PLL_P 4UL -#define PLL_N 1UL -#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P)) // -> 57840000 - -static u8 tda10023_inittab[]={ - // reg mask val - 0x2a,0xff,0x02, // PLL3, Bypass, Power Down - 0xff,0x64,0x00, // Sleep 100ms - 0x2a,0xff,0x03, // PLL3, Bypass, Power Down - 0xff,0x64,0x00, // Sleep 100ms - 0x28,0xff,PLL_M-1, // PLL1 M=8 - 0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1), // PLL2 - 0x00,0xff,0x23, // GPR FSAMPLING=1 - 0x2a,0xff,0x08, // PLL3 PSACLK=1 - 0xff,0x64,0x00, // Sleep 100ms - 0x1f,0xff,0x00, // RESET - 0xff,0x64,0x00, // Sleep 100ms - 0xe6,0x0c,0x04, // RSCFG_IND - 0x10,0xc0,0x80, // DECDVBCFG1 PBER=1 - - 0x0e,0xff,0x82, // GAIN1 - 0x03,0x08,0x08, // CLKCONF DYN=1 - 0x2e,0xbf,0x30, // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0 - 0x01,0xff,0x30, // AGCREF - 0x1e,0x84,0x84, // CONTROL SACLK_ON=1 - 0x1b,0xff,0xc8, // ADC TWOS=1 - 0x3b,0xff,0xff, // IFMAX - 0x3c,0xff,0x00, // IFMIN - 0x34,0xff,0x00, // PWMREF - 0x35,0xff,0xff, // TUNMAX - 0x36,0xff,0x00, // TUNMIN - 0x06,0xff,0x7f, // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 // 0x77 - 0x1c,0x30,0x30, // EQCONF2 STEPALGO=SGNALGO=1 - 0x37,0xff,0xf6, // DELTAF_LSB - 0x38,0xff,0xff, // DELTAF_MSB - 0x02,0xff,0x93, // AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 - 0x2d,0xff,0xf6, // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 - 0x04,0x10,0x00, // SWRAMP=1 - 0x12,0xff,0xa1, // INTP1 POCLKP=1 FEL=1 MFS=0 - 0x2b,0x01,0xa1, // INTS1 - 0x20,0xff,0x04, // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? - 0x2c,0xff,0x0d, // INTP/S TRIP=0 TRIS=0 - 0xc4,0xff,0x00, - 0xc3,0x30,0x00, - 0xb5,0xff,0x19, // ERAGC_THD - 0x00,0x03,0x01, // GPR, CLBS soft reset - 0x00,0x03,0x03, // GPR, CLBS soft reset - 0xff,0x64,0x00, // Sleep 100ms - 0xff,0xff,0xff -}; - static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) { u8 b0 [] = { reg }; @@ -219,30 +173,34 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) s16 SFIL=0; u16 NDEC = 0; - if (sr < (u32)(SYSCLK/98.40)) { + /* avoid floating point operations multiplying syscloc and divider + by 10 */ + u32 sysclk_x_10 = state->sysclk * 10; + + if (sr < (u32)(sysclk_x_10/984)) { NDEC=3; SFIL=1; - } else if (sr<(u32)(SYSCLK/64.0)) { + } else if (sr < (u32)(sysclk_x_10/640)) { NDEC=3; SFIL=0; - } else if (sr<(u32)(SYSCLK/49.2)) { + } else if (sr < (u32)(sysclk_x_10/492)) { NDEC=2; SFIL=1; - } else if (sr<(u32)(SYSCLK/32.0)) { + } else if (sr < (u32)(sysclk_x_10/320)) { NDEC=2; SFIL=0; - } else if (sr<(u32)(SYSCLK/24.6)) { + } else if (sr < (u32)(sysclk_x_10/246)) { NDEC=1; SFIL=1; - } else if (sr<(u32)(SYSCLK/16.0)) { + } else if (sr < (u32)(sysclk_x_10/160)) { NDEC=1; SFIL=0; - } else if (sr<(u32)(SYSCLK/12.3)) { + } else if (sr < (u32)(sysclk_x_10/123)) { NDEC=0; SFIL=1; } - BDRI=SYSCLK*16; + BDRI = (state->sysclk)*16; BDRI>>=NDEC; BDRI +=sr/2; BDRI /=sr; @@ -255,11 +213,12 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) BDRX=1<<(24+NDEC); BDRX*=sr; - do_div(BDRX,SYSCLK); // BDRX/=SYSCLK; + do_div(BDRX, state->sysclk); /* BDRX/=SYSCLK; */ BDR=(s32)BDRX; } -// printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC); + dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n", + sr, BDR, BDRI, NDEC); tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); tda10023_writereg (state, 0x0a, BDR&255); tda10023_writereg (state, 0x0b, (BDR>>8)&255); @@ -272,8 +231,63 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) static int tda10023_init (struct dvb_frontend *fe) { struct tda10023_state* state = fe->demodulator_priv; + u8 tda10023_inittab[] = { +/* reg mask val */ +/* 000 */ 0x2a, 0xff, 0x02, /* PLL3, Bypass, Power Down */ +/* 003 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 006 */ 0x2a, 0xff, 0x03, /* PLL3, Bypass, Power Down */ +/* 009 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ + /* PLL1 */ +/* 012 */ 0x28, 0xff, (state->pll_m-1), + /* PLL2 */ +/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1), + /* GPR FSAMPLING=1 */ +/* 018 */ 0x00, 0xff, REG0_INIT_VAL, +/* 021 */ 0x2a, 0xff, 0x08, /* PLL3 PSACLK=1 */ +/* 024 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 027 */ 0x1f, 0xff, 0x00, /* RESET */ +/* 030 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 033 */ 0xe6, 0x0c, 0x04, /* RSCFG_IND */ +/* 036 */ 0x10, 0xc0, 0x80, /* DECDVBCFG1 PBER=1 */ + +/* 039 */ 0x0e, 0xff, 0x82, /* GAIN1 */ +/* 042 */ 0x03, 0x08, 0x08, /* CLKCONF DYN=1 */ +/* 045 */ 0x2e, 0xbf, 0x30, /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 + PPWMTUN=0 PPWMIF=0 */ +/* 048 */ 0x01, 0xff, 0x30, /* AGCREF */ +/* 051 */ 0x1e, 0x84, 0x84, /* CONTROL SACLK_ON=1 */ +/* 054 */ 0x1b, 0xff, 0xc8, /* ADC TWOS=1 */ +/* 057 */ 0x3b, 0xff, 0xff, /* IFMAX */ +/* 060 */ 0x3c, 0xff, 0x00, /* IFMIN */ +/* 063 */ 0x34, 0xff, 0x00, /* PWMREF */ +/* 066 */ 0x35, 0xff, 0xff, /* TUNMAX */ +/* 069 */ 0x36, 0xff, 0x00, /* TUNMIN */ +/* 072 */ 0x06, 0xff, 0x7f, /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */ +/* 075 */ 0x1c, 0x30, 0x30, /* EQCONF2 STEPALGO=SGNALGO=1 */ +/* 078 */ 0x37, 0xff, 0xf6, /* DELTAF_LSB */ +/* 081 */ 0x38, 0xff, 0xff, /* DELTAF_MSB */ +/* 084 */ 0x02, 0xff, 0x93, /* AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 */ +/* 087 */ 0x2d, 0xff, 0xf6, /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */ +/* 090 */ 0x04, 0x10, 0x00, /* SWRAMP=1 */ +/* 093 */ 0x12, 0xff, 0xa1, /* INTP1 POCLKP=1 FEL=1 MFS=0 */ +/* 096 */ 0x2b, 0x01, 0xa1, /* INTS1 */ +/* 099 */ 0x20, 0xff, 0x04, /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */ +/* 102 */ 0x2c, 0xff, 0x0d, /* INTP/S TRIP=0 TRIS=0 */ +/* 105 */ 0xc4, 0xff, 0x00, +/* 108 */ 0xc3, 0x30, 0x00, +/* 111 */ 0xb5, 0xff, 0x19, /* ERAGC_THD */ +/* 114 */ 0x00, 0x03, 0x01, /* GPR, CLBS soft reset */ +/* 117 */ 0x00, 0x03, 0x03, /* GPR, CLBS soft reset */ +/* 120 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 123 */ 0xff, 0xff, 0xff +}; + dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num); - dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num); + /* override default values if set in config */ + if (state->config->deltaf) { + tda10023_inittab[80] = (state->config->deltaf & 0xff); + tda10023_inittab[83] = (state->config->deltaf >> 8); + } tda10023_writetab(state, tda10023_inittab); @@ -460,12 +474,11 @@ static void tda10023_release(struct dvb_frontend* fe) static struct dvb_frontend_ops tda10023_ops; -struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, - struct i2c_adapter* i2c, +struct dvb_frontend *tda10023_attach(const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm) { struct tda10023_state* state = NULL; - int i; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL); @@ -474,22 +487,40 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, /* setup the state */ state->config = config; state->i2c = i2c; - memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); - state->pwm = pwm; - for (i=0; i < ARRAY_SIZE(tda10023_inittab);i+=3) { - if (tda10023_inittab[i] == 0x00) { - state->reg0 = tda10023_inittab[i+2]; - break; - } - } - // Wakeup if in standby + /* wakeup if in standby */ tda10023_writereg (state, 0x00, 0x33); /* check if the demod is there */ if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; /* create dvb_frontend */ memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + state->reg0 = REG0_INIT_VAL; + if (state->config->xtal) { + state->xtal = state->config->xtal; + state->pll_m = state->config->pll_m; + state->pll_p = state->config->pll_p; + state->pll_n = state->config->pll_n; + } else { + /* set default values if not defined in config */ + state->xtal = 28920000; + state->pll_m = 8; + state->pll_p = 4; + state->pll_n = 1; + } + + /* calc sysclk */ + state->sysclk = (state->xtal * state->pll_m / \ + (state->pll_n * state->pll_p)); + + state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64; + state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4; + + dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n", + __func__, state->xtal, state->pll_m, state->pll_p, + state->pll_n); + state->frontend.demodulator_priv = state; return &state->frontend; @@ -504,10 +535,10 @@ static struct dvb_frontend_ops tda10023_ops = { .name = "Philips TDA10023 DVB-C", .type = FE_QAM, .frequency_stepsize = 62500, - .frequency_min = 47000000, + .frequency_min = 47000000, .frequency_max = 862000000, - .symbol_rate_min = (SYSCLK/2)/64, /* SACLK/64 == (SYSCLK/2)/64 */ - .symbol_rate_max = (SYSCLK/2)/4, /* SACLK/4 */ + .symbol_rate_min = 0, /* set in tda10023_attach */ + .symbol_rate_max = 0, /* set in tda10023_attach */ #if 0 .frequency_tolerance = ???, .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ diff --git a/linux/drivers/media/dvb/frontends/tda1002x.h b/linux/drivers/media/dvb/frontends/tda1002x.h index 1bcc0d44b..4522b7ef5 100644 --- a/linux/drivers/media/dvb/frontends/tda1002x.h +++ b/linux/drivers/media/dvb/frontends/tda1002x.h @@ -26,11 +26,25 @@ #include <linux/dvb/frontend.h> -struct tda1002x_config -{ +struct tda1002x_config { + /* the demodulator's i2c address */ + u8 demod_address; + u8 invert; +}; + +struct tda10023_config { /* the demodulator's i2c address */ u8 demod_address; u8 invert; + + /* clock settings */ + u32 xtal; /* defaults: 28920000 */ + u8 pll_m; /* defaults: 8 */ + u8 pll_p; /* defaults: 4 */ + u8 pll_n; /* defaults: 1 */ + + /* input freq offset + baseband conversion type */ + u16 deltaf; }; #if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) @@ -45,12 +59,15 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* } #endif // CONFIG_DVB_TDA10021 -#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) -extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, - struct i2c_adapter* i2c, u8 pwm); +#if defined(CONFIG_DVB_TDA10023) || \ + (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tda10023_attach( + const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm); #else -static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, - struct i2c_adapter* i2c, u8 pwm) +static inline struct dvb_frontend *tda10023_attach( + const struct tda1002x_config *config, + struct i2c_adapter *i2c, u8 pwm) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c new file mode 100644 index 000000000..f2d66234a --- /dev/null +++ b/linux/drivers/media/dvb/frontends/tda10048.c @@ -0,0 +1,846 @@ +/* + NXP TDA10048HN DVB OFDM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "dvb_frontend.h" +#include "dvb_math.h" +#include "tda10048.h" + +#define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw" +#define TDA10048_DEFAULT_FIRMWARE_SIZE 24878 + +/* Register name definitions */ +#define TDA10048_IDENTITY 0x00 +#define TDA10048_VERSION 0x01 +#define TDA10048_DSP_CODE_CPT 0x0C +#define TDA10048_DSP_CODE_IN 0x0E +#define TDA10048_IN_CONF1 0x10 +#define TDA10048_IN_CONF2 0x11 +#define TDA10048_IN_CONF3 0x12 +#define TDA10048_OUT_CONF1 0x14 +#define TDA10048_OUT_CONF2 0x15 +#define TDA10048_OUT_CONF3 0x16 +#define TDA10048_AUTO 0x18 +#define TDA10048_SYNC_STATUS 0x1A +#define TDA10048_CONF_C4_1 0x1E +#define TDA10048_CONF_C4_2 0x1F +#define TDA10048_CODE_IN_RAM 0x20 +#define TDA10048_CHANNEL_INFO_1_R 0x22 +#define TDA10048_CHANNEL_INFO_2_R 0x23 +#define TDA10048_CHANNEL_INFO1 0x24 +#define TDA10048_CHANNEL_INFO2 0x25 +#define TDA10048_TIME_ERROR_R 0x26 +#define TDA10048_TIME_ERROR 0x27 +#define TDA10048_FREQ_ERROR_LSB_R 0x28 +#define TDA10048_FREQ_ERROR_MSB_R 0x29 +#define TDA10048_FREQ_ERROR_LSB 0x2A +#define TDA10048_FREQ_ERROR_MSB 0x2B +#define TDA10048_IT_SEL 0x30 +#define TDA10048_IT_STAT 0x32 +#define TDA10048_DSP_AD_LSB 0x3C +#define TDA10048_DSP_AD_MSB 0x3D +#define TDA10048_DSP_REF_LSB 0x3E +#define TDA10048_DSP_REF_MSB 0x3F +#define TDA10048_CONF_TRISTATE1 0x44 +#define TDA10048_CONF_TRISTATE2 0x45 +#define TDA10048_CONF_POLARITY 0x46 +#define TDA10048_GPIO_SP_DS0 0x48 +#define TDA10048_GPIO_SP_DS1 0x49 +#define TDA10048_GPIO_SP_DS2 0x4A +#define TDA10048_GPIO_SP_DS3 0x4B +#define TDA10048_GPIO_OUT_SEL 0x4C +#define TDA10048_GPIO_SELECT 0x4D +#define TDA10048_IC_MODE 0x4E +#define TDA10048_CONF_XO 0x50 +#define TDA10048_CONF_PLL1 0x51 +#define TDA10048_CONF_PLL2 0x52 +#define TDA10048_CONF_PLL3 0x53 +#define TDA10048_CONF_ADC 0x54 +#define TDA10048_CONF_ADC_2 0x55 +#define TDA10048_CONF_C1_1 0x60 +#define TDA10048_CONF_C1_3 0x62 +#define TDA10048_AGC_CONF 0x70 +#define TDA10048_AGC_THRESHOLD_LSB 0x72 +#define TDA10048_AGC_THRESHOLD_MSB 0x73 +#define TDA10048_AGC_RENORM 0x74 +#define TDA10048_AGC_GAINS 0x76 +#define TDA10048_AGC_TUN_MIN 0x78 +#define TDA10048_AGC_TUN_MAX 0x79 +#define TDA10048_AGC_IF_MIN 0x7A +#define TDA10048_AGC_IF_MAX 0x7B +#define TDA10048_AGC_TUN_LEVEL 0x7E +#define TDA10048_AGC_IF_LEVEL 0x7F +#define TDA10048_DIG_AGC_LEVEL 0x81 +#define TDA10048_FREQ_PHY2_LSB 0x86 +#define TDA10048_FREQ_PHY2_MSB 0x87 +#define TDA10048_TIME_INVWREF_LSB 0x88 +#define TDA10048_TIME_INVWREF_MSB 0x89 +#define TDA10048_TIME_WREF_LSB 0x8A +#define TDA10048_TIME_WREF_MID1 0x8B +#define TDA10048_TIME_WREF_MID2 0x8C +#define TDA10048_TIME_WREF_MSB 0x8D +#define TDA10048_NP_OUT 0xA2 +#define TDA10048_CELL_ID_LSB 0xA4 +#define TDA10048_CELL_ID_MSB 0xA5 +#define TDA10048_EXTTPS_ODD 0xAA +#define TDA10048_EXTTPS_EVEN 0xAB +#define TDA10048_TPS_LENGTH 0xAC +#define TDA10048_FREE_REG_1 0xB2 +#define TDA10048_FREE_REG_2 0xB3 +#define TDA10048_CONF_C3_1 0xC0 +#define TDA10048_CYBER_CTRL 0xC2 +#define TDA10048_CBER_NMAX_LSB 0xC4 +#define TDA10048_CBER_NMAX_MSB 0xC5 +#define TDA10048_CBER_LSB 0xC6 +#define TDA10048_CBER_MSB 0xC7 +#define TDA10048_VBER_LSB 0xC8 +#define TDA10048_VBER_MID 0xC9 +#define TDA10048_VBER_MSB 0xCA +#define TDA10048_CYBER_LUT 0xCC +#define TDA10048_UNCOR_CTRL 0xCD +#define TDA10048_UNCOR_CPT_LSB 0xCE +#define TDA10048_UNCOR_CPT_MSB 0xCF +#define TDA10048_SOFT_IT_C3 0xD6 +#define TDA10048_CONF_TS2 0xE0 +#define TDA10048_CONF_TS1 0xE1 + +static unsigned int debug; + +#define dprintk(level, fmt, arg...)\ + do { if (debug >= level)\ + printk(KERN_DEBUG "tda10048: " fmt, ## arg);\ + } while (0) + +struct tda10048_state { + + struct i2c_adapter *i2c; + + /* configuration settings */ + const struct tda10048_config *config; + struct dvb_frontend frontend; + + int fwloaded; +}; + +static struct init_tab { + u8 reg; + u16 data; +} init_tab[] = { + { TDA10048_CONF_PLL1, 0x08 }, + { TDA10048_CONF_ADC_2, 0x00 }, + { TDA10048_CONF_C4_1, 0x00 }, + { TDA10048_CONF_PLL1, 0x0f }, + { TDA10048_CONF_PLL2, 0x0a }, + { TDA10048_CONF_PLL3, 0x43 }, + { TDA10048_FREQ_PHY2_LSB, 0x02 }, + { TDA10048_FREQ_PHY2_MSB, 0x0a }, + { TDA10048_TIME_WREF_LSB, 0xbd }, + { TDA10048_TIME_WREF_MID1, 0xe4 }, + { TDA10048_TIME_WREF_MID2, 0xa8 }, + { TDA10048_TIME_WREF_MSB, 0x02 }, + { TDA10048_TIME_INVWREF_LSB, 0x04 }, + { TDA10048_TIME_INVWREF_MSB, 0x06 }, + { TDA10048_CONF_C4_1, 0x00 }, + { TDA10048_CONF_C1_1, 0xa8 }, + { TDA10048_AGC_CONF, 0x16 }, + { TDA10048_CONF_C1_3, 0x0b }, + { TDA10048_AGC_TUN_MIN, 0x00 }, + { TDA10048_AGC_TUN_MAX, 0xff }, + { TDA10048_AGC_IF_MIN, 0x00 }, + { TDA10048_AGC_IF_MAX, 0xff }, + { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, + { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, + { TDA10048_CYBER_CTRL, 0x38 }, + { TDA10048_AGC_GAINS, 0x12 }, + { TDA10048_CONF_XO, 0x00 }, + { TDA10048_CONF_TS1, 0x07 }, + { TDA10048_IC_MODE, 0x00 }, + { TDA10048_CONF_TS2, 0xc0 }, + { TDA10048_CONF_TRISTATE1, 0x21 }, + { TDA10048_CONF_TRISTATE2, 0x00 }, + { TDA10048_CONF_POLARITY, 0x00 }, + { TDA10048_CONF_C4_2, 0x04 }, + { TDA10048_CONF_ADC, 0x60 }, + { TDA10048_CONF_ADC_2, 0x10 }, + { TDA10048_CONF_ADC, 0x60 }, + { TDA10048_CONF_ADC_2, 0x00 }, + { TDA10048_CONF_C1_1, 0xa8 }, + { TDA10048_UNCOR_CTRL, 0x00 }, + { TDA10048_CONF_C4_2, 0x04 }, +}; + +static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + + dprintk(2, "%s(reg = 0x%02x, data = 0x%02x)\n", __func__, reg, data); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (ret == %i)\n", __func__, ret); + + return (ret != 1) ? -1 : 0; +} + +static u8 tda10048_readreg(struct tda10048_state *state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { + { .addr = state->config->demod_address, + .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, + .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + dprintk(2, "%s(reg = 0x%02x)\n", __func__, reg); + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + printk(KERN_ERR "%s: readreg error (ret == %i)\n", + __func__, ret); + + return b1[0]; +} + +static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg, + u8 *data, u16 len) +{ + int ret = -EREMOTEIO; + struct i2c_msg msg; + u8 *buf; + + dprintk(2, "%s(%d, ?, len = %d)\n", __func__, reg, len); + + buf = kmalloc(len + 1, GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + goto error; + } + + *buf = reg; + memcpy(buf + 1, data, len); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = len + 1; + + dprintk(2, "%s(): write len = %d\n", + __func__, msg.len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s(): writereg error err %i\n", + __func__, ret); + ret = -EREMOTEIO; + } + +error: + kfree(buf); + + return ret; +} + +static int tda10048_firmware_upload(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret; + int pos = 0; + int cnt; + u8 wlen = state->config->fwbulkwritelen; + + if ((wlen != TDA10048_BULKWRITE_200) && (wlen != TDA10048_BULKWRITE_50)) + wlen = TDA10048_BULKWRITE_200; + + /* request the firmware, this will block and timeout */ + printk(KERN_INFO "%s: waiting for firmware upload (%s)...\n", + __func__, + TDA10048_DEFAULT_FIRMWARE); + + ret = request_firmware(&fw, TDA10048_DEFAULT_FIRMWARE, + &state->i2c->dev); + if (ret) { + printk(KERN_ERR "%s: Upload failed. (file not found?)\n", + __func__); + return -EIO; + } else { + printk(KERN_INFO "%s: firmware read %Zu bytes.\n", + __func__, + fw->size); + ret = 0; + } + + if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { + printk(KERN_ERR "%s: firmware incorrect size\n", __func__); + return -EIO; + } else { + printk(KERN_INFO "%s: firmware uploading\n", __func__); + + /* Soft reset */ + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, + tda10048_readreg(state, TDA10048_CONF_TRISTATE1) + & 0xfe); + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, + tda10048_readreg(state, TDA10048_CONF_TRISTATE1) + | 0x01); + + /* Put the demod into host download mode */ + tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xf9); + + /* Boot the DSP */ + tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x08); + + /* Prepare for download */ + tda10048_writereg(state, TDA10048_DSP_CODE_CPT, 0); + + /* Download the firmware payload */ + while (pos < fw->size) { + + if ((fw->size - pos) > wlen) + cnt = wlen; + else + cnt = fw->size - pos; + + tda10048_writeregbulk(state, TDA10048_DSP_CODE_IN, + &fw->data[pos], cnt); + + pos += cnt; + } + + ret = -EIO; + /* Wait up to 250ms for the DSP to boot */ + for (cnt = 0; cnt < 250 ; cnt += 10) { + + msleep(10); + + if (tda10048_readreg(state, TDA10048_SYNC_STATUS) + & 0x40) { + ret = 0; + break; + } + } + } + + release_firmware(fw); + + if (ret == 0) { + printk(KERN_INFO "%s: firmware uploaded\n", __func__); + state->fwloaded = 1; + } else + printk(KERN_ERR "%s: firmware upload failed\n", __func__); + + return ret; +} + +static int tda10048_set_inversion(struct dvb_frontend *fe, int inversion) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s(%d)\n", __func__, inversion); + + if (inversion == TDA10048_INVERSION_ON) + tda10048_writereg(state, TDA10048_CONF_C1_1, + tda10048_readreg(state, TDA10048_CONF_C1_1) | 0x20); + else + tda10048_writereg(state, TDA10048_CONF_C1_1, + tda10048_readreg(state, TDA10048_CONF_C1_1) & 0xdf); + + return 0; +} + +/* Retrieve the demod settings */ +static int tda10048_get_tps(struct tda10048_state *state, + struct dvb_ofdm_parameters *p) +{ + u8 val; + + /* Make sure the TPS regs are valid */ + if (!(tda10048_readreg(state, TDA10048_AUTO) & 0x01)) + return -EAGAIN; + + val = tda10048_readreg(state, TDA10048_OUT_CONF2); + switch ((val & 0x60) >> 5) { + case 0: p->constellation = QPSK; break; + case 1: p->constellation = QAM_16; break; + case 2: p->constellation = QAM_64; break; + } + switch ((val & 0x18) >> 3) { + case 0: p->hierarchy_information = HIERARCHY_NONE; break; + case 1: p->hierarchy_information = HIERARCHY_1; break; + case 2: p->hierarchy_information = HIERARCHY_2; break; + case 3: p->hierarchy_information = HIERARCHY_4; break; + } + switch (val & 0x07) { + case 0: p->code_rate_HP = FEC_1_2; break; + case 1: p->code_rate_HP = FEC_2_3; break; + case 2: p->code_rate_HP = FEC_3_4; break; + case 3: p->code_rate_HP = FEC_5_6; break; + case 4: p->code_rate_HP = FEC_7_8; break; + } + + val = tda10048_readreg(state, TDA10048_OUT_CONF3); + switch (val & 0x07) { + case 0: p->code_rate_LP = FEC_1_2; break; + case 1: p->code_rate_LP = FEC_2_3; break; + case 2: p->code_rate_LP = FEC_3_4; break; + case 3: p->code_rate_LP = FEC_5_6; break; + case 4: p->code_rate_LP = FEC_7_8; break; + } + + val = tda10048_readreg(state, TDA10048_OUT_CONF1); + switch ((val & 0x0c) >> 2) { + case 0: p->guard_interval = GUARD_INTERVAL_1_32; break; + case 1: p->guard_interval = GUARD_INTERVAL_1_16; break; + case 2: p->guard_interval = GUARD_INTERVAL_1_8; break; + case 3: p->guard_interval = GUARD_INTERVAL_1_4; break; + } + switch (val & 0x02) { + case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break; + } + + return 0; +} + +static int tda10048_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s(%d)\n", __func__, enable); + + if (enable) + return tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x02); + else + return tda10048_writereg(state, TDA10048_CONF_C4_1, + tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xfd); +} + +static int tda10048_output_mode(struct dvb_frontend *fe, int serial) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s(%d)\n", __func__, serial); + + /* Ensure pins are out of tri-state */ + tda10048_writereg(state, TDA10048_CONF_TRISTATE1, 0x21); + tda10048_writereg(state, TDA10048_CONF_TRISTATE2, 0x00); + + if (serial) { + tda10048_writereg(state, TDA10048_IC_MODE, 0x80 | 0x20); + tda10048_writereg(state, TDA10048_CONF_TS2, 0xc0); + } else { + tda10048_writereg(state, TDA10048_IC_MODE, 0x00); + tda10048_writereg(state, TDA10048_CONF_TS2, 0x01); + } + + return 0; +} + +/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +/* TODO: Support manual tuning with specific params */ +static int tda10048_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s(frequency=%d)\n", __func__, p->frequency); + + if (fe->ops.tuner_ops.set_params) { + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + fe->ops.tuner_ops.set_params(fe, p); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Enable demod TPS auto detection and begin acquisition */ + tda10048_writereg(state, TDA10048_AUTO, 0x57); + + return 0; +} + +/* Establish sane defaults and load firmware. */ +static int tda10048_init(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + int ret = 0, i; + + dprintk(1, "%s()\n", __func__); + + /* Apply register defaults */ + for (i = 0; i < ARRAY_SIZE(init_tab); i++) + tda10048_writereg(state, init_tab[i].reg, init_tab[i].data); + + if (state->fwloaded == 0) + ret = tda10048_firmware_upload(fe); + + /* Set either serial or parallel */ + tda10048_output_mode(fe, state->config->output_mode); + + /* set inversion */ + tda10048_set_inversion(fe, state->config->inversion); + + /* Ensure we leave the gate closed */ + tda10048_i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int tda10048_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 reg; + + *status = 0; + + reg = tda10048_readreg(state, TDA10048_SYNC_STATUS); + + dprintk(1, "%s() status =0x%02x\n", __func__, reg); + + if (reg & 0x02) + *status |= FE_HAS_CARRIER; + + if (reg & 0x04) + *status |= FE_HAS_SIGNAL; + + if (reg & 0x08) { + *status |= FE_HAS_LOCK; + *status |= FE_HAS_VITERBI; + *status |= FE_HAS_SYNC; + } + + return 0; +} + +static int tda10048_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + /* TODO: A reset may be required here */ + *ber = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | + tda10048_readreg(state, TDA10048_CBER_LSB); + + return 0; +} + +static int tda10048_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 v; + + dprintk(1, "%s()\n", __func__); + + *signal_strength = 65535; + + v = tda10048_readreg(state, TDA10048_NP_OUT); + if (v > 0) + *signal_strength -= (v << 8) | v; + + return 0; +} + +/* SNR lookup table */ +static struct snr_tab { + u8 val; + u8 data; +} snr_tab[] = { + { 0, 0 }, + { 1, 246 }, + { 2, 215 }, + { 3, 198 }, + { 4, 185 }, + { 5, 176 }, + { 6, 168 }, + { 7, 161 }, + { 8, 155 }, + { 9, 150 }, + { 10, 146 }, + { 11, 141 }, + { 12, 138 }, + { 13, 134 }, + { 14, 131 }, + { 15, 128 }, + { 16, 125 }, + { 17, 122 }, + { 18, 120 }, + { 19, 118 }, + { 20, 115 }, + { 21, 113 }, + { 22, 111 }, + { 23, 109 }, + { 24, 107 }, + { 25, 106 }, + { 26, 104 }, + { 27, 102 }, + { 28, 101 }, + { 29, 99 }, + { 30, 98 }, + { 31, 96 }, + { 32, 95 }, + { 33, 94 }, + { 34, 92 }, + { 35, 91 }, + { 36, 90 }, + { 37, 89 }, + { 38, 88 }, + { 39, 86 }, + { 40, 85 }, + { 41, 84 }, + { 42, 83 }, + { 43, 82 }, + { 44, 81 }, + { 45, 80 }, + { 46, 79 }, + { 47, 78 }, + { 48, 77 }, + { 49, 76 }, + { 50, 76 }, + { 51, 75 }, + { 52, 74 }, + { 53, 73 }, + { 54, 72 }, + { 56, 71 }, + { 57, 70 }, + { 58, 69 }, + { 60, 68 }, + { 61, 67 }, + { 63, 66 }, + { 64, 65 }, + { 66, 64 }, + { 67, 63 }, + { 68, 62 }, + { 69, 62 }, + { 70, 61 }, + { 72, 60 }, + { 74, 59 }, + { 75, 58 }, + { 77, 57 }, + { 79, 56 }, + { 81, 55 }, + { 83, 54 }, + { 85, 53 }, + { 87, 52 }, + { 89, 51 }, + { 91, 50 }, + { 93, 49 }, + { 95, 48 }, + { 97, 47 }, + { 100, 46 }, + { 102, 45 }, + { 104, 44 }, + { 107, 43 }, + { 109, 42 }, + { 112, 41 }, + { 114, 40 }, + { 117, 39 }, + { 120, 38 }, + { 123, 37 }, + { 125, 36 }, + { 128, 35 }, + { 131, 34 }, + { 134, 33 }, + { 138, 32 }, + { 141, 31 }, + { 144, 30 }, + { 147, 29 }, + { 151, 28 }, + { 154, 27 }, + { 158, 26 }, + { 162, 25 }, + { 165, 24 }, + { 169, 23 }, + { 173, 22 }, + { 177, 21 }, + { 181, 20 }, + { 186, 19 }, + { 190, 18 }, + { 194, 17 }, + { 199, 16 }, + { 204, 15 }, + { 208, 14 }, + { 213, 13 }, + { 218, 12 }, + { 223, 11 }, + { 229, 10 }, + { 234, 9 }, + { 239, 8 }, + { 245, 7 }, + { 251, 6 }, + { 255, 5 }, +}; + +static int tda10048_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct tda10048_state *state = fe->demodulator_priv; + u8 v; + int i, ret = -EINVAL; + + dprintk(1, "%s()\n", __func__); + + v = tda10048_readreg(state, TDA10048_NP_OUT); + for (i = 0; i < ARRAY_SIZE(snr_tab); i++) { + if (v <= snr_tab[i].val) { + *snr = snr_tab[i].data; + ret = 0; + break; + } + } + + return ret; +} + +static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); +#if 0 + /* Reset and begin counting */ + tda10048_writereg(state, TDA10048_UNCOR_CTRL, 0x01); + msleep(10); +#endif + + *ucblocks = tda10048_readreg(state, TDA10048_UNCOR_CPT_MSB) << 8 | + tda10048_readreg(state, TDA10048_UNCOR_CPT_LSB); + + return 0; +} + +static int tda10048_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10048_state *state = fe->demodulator_priv; + + dprintk(1, "%s()\n", __func__); + + p->inversion = tda10048_readreg(state, TDA10048_CONF_C1_1) + & 0x20 ? INVERSION_ON : INVERSION_OFF; + + return tda10048_get_tps(state, &p->u.ofdm); +} + +static int tda10048_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void tda10048_release(struct dvb_frontend *fe) +{ + struct tda10048_state *state = fe->demodulator_priv; + dprintk(1, "%s()\n", __func__); + kfree(state); +} + +static struct dvb_frontend_ops tda10048_ops; + +struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, + struct i2c_adapter *i2c) +{ + struct tda10048_state *state = NULL; + + dprintk(1, "%s()\n", __func__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->fwloaded = 0; + + /* check if the demod is present */ + if (tda10048_readreg(state, TDA10048_IDENTITY) != 0x048) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10048_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + /* Leave the gate closed */ + tda10048_i2c_gate_ctrl(&state->frontend, 0); + + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(tda10048_attach); + +static struct dvb_frontend_ops tda10048_ops = { + + .info = { + .name = "NXP TDA10048HN DVB-T", + .type = FE_OFDM, + .frequency_min = 177000000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + + .release = tda10048_release, + .init = tda10048_init, + .i2c_gate_ctrl = tda10048_i2c_gate_ctrl, + .set_frontend = tda10048_set_frontend, + .get_frontend = tda10048_get_frontend, + .get_tune_settings = tda10048_get_tune_settings, + .read_status = tda10048_read_status, + .read_ber = tda10048_read_ber, + .read_signal_strength = tda10048_read_signal_strength, + .read_snr = tda10048_read_snr, + .read_ucblocks = tda10048_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_DESCRIPTION("NXP TDA10048HN DVB-T Demodulator driver"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/tda10048.h b/linux/drivers/media/dvb/frontends/tda10048.h new file mode 100644 index 000000000..2b5c78e62 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/tda10048.h @@ -0,0 +1,63 @@ +/* + NXP TDA10048HN DVB OFDM demodulator driver + + Copyright (C) 2008 Steven Toth <stoth@hauppauge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef TDA10048_H +#define TDA10048_H + +#include <linux/dvb/frontend.h> +#include <linux/firmware.h> + +struct tda10048_config { + + /* the demodulator's i2c address */ + u8 demod_address; + + /* serial/parallel output */ +#define TDA10048_PARALLEL_OUTPUT 0 +#define TDA10048_SERIAL_OUTPUT 1 + u8 output_mode; + +#define TDA10048_BULKWRITE_200 200 +#define TDA10048_BULKWRITE_50 50 + u8 fwbulkwritelen; + + /* Spectral Inversion */ +#define TDA10048_INVERSION_OFF 0 +#define TDA10048_INVERSION_ON 1 + u8 inversion; +}; + +#if defined(CONFIG_DVB_TDA10048) || \ + (defined(CONFIG_DVB_TDA10048_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tda10048_attach( + const struct tda10048_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *tda10048_attach( + const struct tda10048_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_TDA10048 */ + +#endif /* TDA10048_H */ diff --git a/linux/drivers/media/dvb/frontends/tda10086.c b/linux/drivers/media/dvb/frontends/tda10086.c index 61ea88519..c749f96c2 100644 --- a/linux/drivers/media/dvb/frontends/tda10086.c +++ b/linux/drivers/media/dvb/frontends/tda10086.c @@ -91,16 +91,16 @@ static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, { int val; - // read a byte and check + /* read a byte and check */ val = tda10086_read_byte(state, reg); if (val < 0) return val; - // mask if off + /* mask if off */ val = val & ~mask; val |= data & 0xff; - // write it out again + /* write it out again */ return tda10086_write_byte(state, reg, val); } @@ -113,58 +113,63 @@ static int tda10086_init(struct dvb_frontend* fe) if (state->config->diseqc_tone) t22k_off = 0; - // reset + /* reset */ tda10086_write_byte(state, 0x00, 0x00); msleep(10); - // misc setup + /* misc setup */ tda10086_write_byte(state, 0x01, 0x94); - tda10086_write_byte(state, 0x02, 0x35); // NOTE: TT drivers appear to disable CSWP + tda10086_write_byte(state, 0x02, 0x35); /* NOTE: TT drivers appear to disable CSWP */ tda10086_write_byte(state, 0x03, 0xe4); tda10086_write_byte(state, 0x04, 0x43); tda10086_write_byte(state, 0x0c, 0x0c); - tda10086_write_byte(state, 0x1b, 0xb0); // noise threshold - tda10086_write_byte(state, 0x20, 0x89); // misc - tda10086_write_byte(state, 0x30, 0x04); // acquisition period length - tda10086_write_byte(state, 0x32, 0x00); // irq off - tda10086_write_byte(state, 0x31, 0x56); // setup AFC - - // setup PLL (assumes 16Mhz XIN) - tda10086_write_byte(state, 0x55, 0x2c); // misc PLL setup - tda10086_write_byte(state, 0x3a, 0x0b); // M=12 - tda10086_write_byte(state, 0x3b, 0x01); // P=2 - tda10086_write_mask(state, 0x55, 0x20, 0x00); // powerup PLL - - // setup TS interface + tda10086_write_byte(state, 0x1b, 0xb0); /* noise threshold */ + tda10086_write_byte(state, 0x20, 0x89); /* misc */ + tda10086_write_byte(state, 0x30, 0x04); /* acquisition period length */ + tda10086_write_byte(state, 0x32, 0x00); /* irq off */ + tda10086_write_byte(state, 0x31, 0x56); /* setup AFC */ + + /* setup PLL (this assumes SACLK = 96MHz) */ + tda10086_write_byte(state, 0x55, 0x2c); /* misc PLL setup */ + if (state->config->xtal_freq == TDA10086_XTAL_16M) { + tda10086_write_byte(state, 0x3a, 0x0b); /* M=12 */ + tda10086_write_byte(state, 0x3b, 0x01); /* P=2 */ + } else { + tda10086_write_byte(state, 0x3a, 0x17); /* M=24 */ + tda10086_write_byte(state, 0x3b, 0x00); /* P=1 */ + } + tda10086_write_mask(state, 0x55, 0x20, 0x00); /* powerup PLL */ + + /* setup TS interface */ tda10086_write_byte(state, 0x11, 0x81); tda10086_write_byte(state, 0x12, 0x81); - tda10086_write_byte(state, 0x19, 0x40); // parallel mode A + MSBFIRST - tda10086_write_byte(state, 0x56, 0x80); // powerdown WPLL - unused in the mode we use - tda10086_write_byte(state, 0x57, 0x08); // bypass WPLL - unused in the mode we use + tda10086_write_byte(state, 0x19, 0x40); /* parallel mode A + MSBFIRST */ + tda10086_write_byte(state, 0x56, 0x80); /* powerdown WPLL - unused in the mode we use */ + tda10086_write_byte(state, 0x57, 0x08); /* bypass WPLL - unused in the mode we use */ tda10086_write_byte(state, 0x10, 0x2a); - // setup ADC - tda10086_write_byte(state, 0x58, 0x61); // ADC setup - tda10086_write_mask(state, 0x58, 0x01, 0x00); // powerup ADC + /* setup ADC */ + tda10086_write_byte(state, 0x58, 0x61); /* ADC setup */ + tda10086_write_mask(state, 0x58, 0x01, 0x00); /* powerup ADC */ - // setup AGC + /* setup AGC */ tda10086_write_byte(state, 0x05, 0x0B); tda10086_write_byte(state, 0x37, 0x63); - tda10086_write_byte(state, 0x3f, 0x0a); // NOTE: flydvb varies it + tda10086_write_byte(state, 0x3f, 0x0a); /* NOTE: flydvb varies it */ tda10086_write_byte(state, 0x40, 0x64); tda10086_write_byte(state, 0x41, 0x4f); tda10086_write_byte(state, 0x42, 0x43); - // setup viterbi - tda10086_write_byte(state, 0x1a, 0x11); // VBER 10^6, DVB, QPSK + /* setup viterbi */ + tda10086_write_byte(state, 0x1a, 0x11); /* VBER 10^6, DVB, QPSK */ - // setup carrier recovery + /* setup carrier recovery */ tda10086_write_byte(state, 0x3d, 0x80); - // setup SEC - tda10086_write_byte(state, 0x36, t22k_off); // all SEC off, 22k tone - tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); // } tone frequency - tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); // } + /* setup SEC */ + tda10086_write_byte(state, 0x36, t22k_off); /* all SEC off, 22k tone */ + tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); + tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); return 0; } @@ -303,7 +308,7 @@ static int tda10086_set_symbol_rate(struct tda10086_state *state, dprintk ("%s %i\n", __func__, symbol_rate); - // setup the decimation and anti-aliasing filters.. + /* setup the decimation and anti-aliasing filters.. */ if (symbol_rate < (u32) (SACLK * 0.0137)) { dfn=4; afs=1; @@ -340,13 +345,13 @@ static int tda10086_set_symbol_rate(struct tda10086_state *state, byp=1; } - // calculate BDR + /* calculate BDR */ big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<<dfn); big += ((SACLK/1000ULL)-1ULL); do_div(big, (SACLK/1000ULL)); bdr = big & 0xfffff; - // calculate BDRI + /* calculate BDRI */ tmp = (1<<dfn)*(symbol_rate/1000); bdri = ((32 * (SACLK/1000)) + (tmp-1)) / tmp; @@ -415,11 +420,11 @@ static int tda10086_set_frontend(struct dvb_frontend* fe, dprintk ("%s\n", __func__); - // modify parameters for tuning + /* modify parameters for tuning */ tda10086_write_byte(state, 0x02, 0x35); state->has_lock = false; - // set params + /* set params */ if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe, fe_params); if (fe->ops.i2c_gate_ctrl) @@ -431,7 +436,7 @@ static int tda10086_set_frontend(struct dvb_frontend* fe, fe->ops.i2c_gate_ctrl(fe, 0); } - // calcluate the frequency offset (in *Hz* not kHz) + /* calcluate the frequency offset (in *Hz* not kHz) */ freqoff = fe_params->frequency - freq; freqoff = ((1<<16) * freqoff) / (SACLK/1000); tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f)); @@ -444,7 +449,7 @@ static int tda10086_set_frontend(struct dvb_frontend* fe, if ((ret = tda10086_set_fec(state, fe_params)) < 0) return ret; - // soft reset + disable TS output until lock + /* soft reset + disable TS output until lock */ tda10086_write_mask(state, 0x10, 0x40, 0x40); tda10086_write_mask(state, 0x00, 0x01, 0x00); @@ -462,11 +467,11 @@ static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa dprintk ("%s\n", __func__); - // check for invalid symbol rate + /* check for invalid symbol rate */ if (fe_params->u.qpsk.symbol_rate < 500000) return -EINVAL; - // calculate the updated frequency (note: we convert from Hz->kHz) + /* calculate the updated frequency (note: we convert from Hz->kHz) */ tmp64 = tda10086_read_byte(state, 0x52); tmp64 |= (tda10086_read_byte(state, 0x51) << 8); if (tmp64 & 0x8000) @@ -475,7 +480,7 @@ static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa do_div(tmp64, (1ULL<<15) * (1ULL<<1)); fe_params->frequency = (int) state->frequency + (int) tmp64; - // the inversion + /* the inversion */ val = tda10086_read_byte(state, 0x0c); if (val & 0x80) { switch(val & 0x40) { @@ -506,7 +511,7 @@ static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa } } - // calculate the updated symbol rate + /* calculate the updated symbol rate */ tmp = tda10086_read_byte(state, 0x1d); if (tmp & 0x80) tmp |= 0xffffff00; @@ -514,7 +519,7 @@ static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000); fe_params->u.qpsk.symbol_rate = state->symbol_rate + tmp; - // the FEC + /* the FEC */ val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4; switch(val) { case 0x00: @@ -567,7 +572,7 @@ static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status) *fe_status |= FE_HAS_LOCK; if (!state->has_lock) { state->has_lock = true; - // modify parameters for stable reception + /* modify parameters for stable reception */ tda10086_write_byte(state, 0x02, 0x00); } } @@ -607,10 +612,10 @@ static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) dprintk ("%s\n", __func__); - // read it + /* read it */ *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f; - // reset counter + /* reset counter */ tda10086_write_byte(state, 0x18, 0x00); tda10086_write_byte(state, 0x18, 0x80); @@ -623,7 +628,7 @@ static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber) dprintk ("%s\n", __func__); - // read it + /* read it */ *ber = 0; *ber |= tda10086_read_byte(state, 0x15); *ber |= tda10086_read_byte(state, 0x16) << 8; diff --git a/linux/drivers/media/dvb/frontends/tda10086.h b/linux/drivers/media/dvb/frontends/tda10086.h index 197c237e5..61148c558 100644 --- a/linux/drivers/media/dvb/frontends/tda10086.h +++ b/linux/drivers/media/dvb/frontends/tda10086.h @@ -26,6 +26,11 @@ #include <linux/dvb/frontend.h> #include <linux/firmware.h> +enum tda10086_xtal { + TDA10086_XTAL_16M, + TDA10086_XTAL_4M +}; + struct tda10086_config { /* the demodulator's i2c address */ @@ -36,6 +41,9 @@ struct tda10086_config /* do we need the diseqc signal with carrier? */ u8 diseqc_tone; + + /* frequency of the reference xtal */ + enum tda10086_xtal xtal_freq; }; #if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE)) @@ -48,6 +56,6 @@ static inline struct dvb_frontend* tda10086_attach(const struct tda10086_config* printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } -#endif // CONFIG_DVB_TDA10086 +#endif /* CONFIG_DVB_TDA10086 */ -#endif // TDA10086_H +#endif /* TDA10086_H */ diff --git a/linux/drivers/media/dvb/frontends/tda826x.c b/linux/drivers/media/dvb/frontends/tda826x.c index a6540e7a2..596fc4275 100644 --- a/linux/drivers/media/dvb/frontends/tda826x.c +++ b/linux/drivers/media/dvb/frontends/tda826x.c @@ -76,6 +76,8 @@ static int tda826x_set_params(struct dvb_frontend *fe, struct dvb_frontend_param struct tda826x_priv *priv = fe->tuner_priv; int ret; u32 div; + u32 ksyms; + u32 bandwidth; u8 buf [11]; struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 }; @@ -83,6 +85,15 @@ static int tda826x_set_params(struct dvb_frontend *fe, struct dvb_frontend_param div = (params->frequency + (1000-1)) / 1000; + /* BW = ((1 + RO) * SR/2 + 5) * 1.3 [SR in MSPS, BW in MHz] */ + /* with R0 = 0.35 and some transformations: */ + ksyms = params->u.qpsk.symbol_rate / 1000; + bandwidth = (878 * ksyms + 6500000) / 1000000 + 1; + if (bandwidth < 5) + bandwidth = 5; + else if (bandwidth > 36) + bandwidth = 36; + buf[0] = 0x00; // subaddress buf[1] = 0x09; // powerdown RSSI + the magic value 1 if (!priv->has_loopthrough) @@ -90,7 +101,7 @@ static int tda826x_set_params(struct dvb_frontend *fe, struct dvb_frontend_param buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO buf[3] = div >> 7; buf[4] = div << 1; - buf[5] = 0x77; // baseband cut-off 19 MHz + buf[5] = ((bandwidth - 5) << 3) | 7; /* baseband cut-off */ buf[6] = 0xfe; // baseband gain 9 db + no RF attenuation buf[7] = 0x83; // charge pumps at high, tests off buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports. diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index ae882432d..d4339b1b3 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -5,6 +5,7 @@ config TTPCI_EEPROM config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C + depends on HOTPLUG select FW_LOADER if !DVB_AV7110_FIRMWARE select TTPCI_EEPROM select VIDEO_SAA7146_VV @@ -123,6 +124,7 @@ config DVB_BUDGET_AV depends on DVB_BUDGET_CORE && I2C select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV + depends on HOTPLUG # dependency of FW_LOADER select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index ed7fb1df5..b7d1f2f18 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -577,7 +577,7 @@ static struct stv0299_config typhoon_config = { .mclk = 88000000UL, .invert = 0, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_1, + .lock_output = STV0299_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, @@ -590,7 +590,7 @@ static struct stv0299_config cinergy_1200s_config = { .mclk = 88000000UL, .invert = 0, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_0, + .lock_output = STV0299_LOCKOUTPUT_0, .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, @@ -602,7 +602,7 @@ static struct stv0299_config cinergy_1200s_1894_0010_config = { .mclk = 88000000UL, .invert = 1, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_1, + .lock_output = STV0299_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, @@ -667,6 +667,11 @@ static struct tda1002x_config philips_cu1216_config_altaddress = { .invert = 0, }; +static struct tda10023_config philips_cu1216_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; @@ -869,7 +874,7 @@ static struct stv0299_config philips_sd1878_config = { .mclk = 88000000UL, .invert = 0, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_1, + .lock_output = STV0299_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, @@ -1019,9 +1024,10 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBC_KNC1_PLUS_MK3: budget_av->reinitialise_demod = 1; budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - fe = dvb_attach(tda10023_attach, &philips_cu1216_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); + fe = dvb_attach(tda10023_attach, + &philips_cu1216_tda10023_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); if (fe) { fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; } diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index 734b91b98..8c6ba2edc 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -738,7 +738,7 @@ static struct stv0299_config philips_su1278_tt_config = { .mclk = 64000000UL, .invert = 0, .skip_reinit = 1, - .lock_output = STV0229_LOCKOUTPUT_1, + .lock_output = STV0299_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 50, .set_symbol_rate = philips_su1278_tt_set_symbol_rate, diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index e36c32528..2293d80c6 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -45,6 +45,7 @@ #include "tda826x.h" #include "lnbp21.h" #include "bsru6.h" +#include "bsbe1.h" static int diseqc_method; module_param(diseqc_method, int, 0444); @@ -365,8 +366,41 @@ static struct tda10086_config tda10086_config = { .demod_address = 0x0e, .invert = 0, .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, }; +static struct stv0299_config alps_bsru6_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, +}; + +static struct stv0299_config alps_bsbe1_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsbe1_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsbe1_set_symbol_rate, +}; + + +static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) +{ + u8 val; + struct i2c_msg msg[] = { + { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } + }; + + return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; +} + static u8 read_pwm(struct budget* budget) { u8 b = 0xff; @@ -382,6 +416,8 @@ static u8 read_pwm(struct budget* budget) static void frontend_init(struct budget *budget) { + (void)alps_bsbe1_config; /* avoid warning */ + switch(budget->dev->pci->subsystem_device) { case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) case 0x1013: @@ -427,15 +463,43 @@ static void frontend_init(struct budget *budget) } break; - case 0x4f60: // Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/ALPS BSRU6(tsa5059)) - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; - budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ + { + int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); + + if (subtype < 0) + break; + /* fixme: find a better way to identify the card */ + if (subtype < 0x36) { + /* assume ALPS BSRU6 */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } else { + /* assume ALPS BSBE1 */ + /* reset tuner */ + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); + msleep(250); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } } break; + } case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 0c0149af6..6716ab2b1 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1326,7 +1326,7 @@ static struct stv0299_config alps_stv0299_config = { .mclk = 88000000UL, .invert = 1, .skip_reinit = 0, - .lock_output = STV0229_LOCKOUTPUT_1, + .lock_output = STV0299_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = alps_stv0299_set_symbol_rate, diff --git a/linux/drivers/media/dvb/ttusb-dec/Kconfig b/linux/drivers/media/dvb/ttusb-dec/Kconfig index 83611012e..a23cc0aa1 100644 --- a/linux/drivers/media/dvb/ttusb-dec/Kconfig +++ b/linux/drivers/media/dvb/ttusb-dec/Kconfig @@ -1,6 +1,7 @@ config DVB_TTUSB_DEC tristate "Technotrend/Hauppauge USB DEC devices" - depends on DVB_CORE && USB + depends on DVB_CORE && USB && INPUT + depends on HOTPLUG # due to FW_LOADER select FW_LOADER select CRC32 help diff --git a/linux/drivers/media/radio/radio-aimslab.c b/linux/drivers/media/radio/radio-aimslab.c index 25e59032f..7b9afd7e6 100644 --- a/linux/drivers/media/radio/radio-aimslab.c +++ b/linux/drivers/media/radio/radio-aimslab.c @@ -37,7 +37,6 @@ #include "compat.h" #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> -#include <asm/semaphore.h> /* Lock for the I/O */ #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,0,2) diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 54b9f84e9..89d8d3783 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -1,4 +1,54 @@ # +# Generic video config states +# + +config VIDEO_V4L2 + tristate + depends on VIDEO_DEV && VIDEO_V4L2_COMMON + default VIDEO_DEV && VIDEO_V4L2_COMMON + +config VIDEO_V4L1 + tristate + depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 + default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 + +config VIDEOBUF_GEN + tristate + +config VIDEOBUF_DMA_SG + depends on HAS_DMA + select VIDEOBUF_GEN + tristate + +config VIDEOBUF_VMALLOC + select VIDEOBUF_GEN + tristate + +config VIDEOBUF_DVB + tristate + select VIDEOBUF_GEN + select VIDEOBUF_DMA_SG + +config VIDEO_BTCX + tristate + +config VIDEO_IR_I2C + tristate + +config VIDEO_IR + tristate + depends on INPUT + select VIDEO_IR_I2C if I2C + +config VIDEO_TVEEPROM + tristate + depends on I2C + +config VIDEO_TUNER + tristate + depends on MEDIA_TUNER + +# # Multimedia Video device configuration # @@ -698,8 +748,12 @@ source "drivers/media/video/cx88/Kconfig" source "drivers/media/video/cx23885/Kconfig" +source "drivers/media/video/au0828/Kconfig" + source "drivers/media/video/ivtv/Kconfig" +source "drivers/media/video/cx18/Kconfig" + config VIDEO_M32R_AR tristate "AR devices" depends on M32R && VIDEO_V4L1 @@ -856,7 +910,7 @@ config SOC_CAMERA config SOC_CAMERA_MT9M001 tristate "mt9m001 support" - depends on SOC_CAMERA + depends on SOC_CAMERA && I2C select GPIO_PCA953X if MT9M001_PCA9536_SWITCH help This driver supports MT9M001 cameras from Micron, monochrome @@ -871,7 +925,7 @@ config MT9M001_PCA9536_SWITCH config SOC_CAMERA_MT9V022 tristate "mt9v022 support" - depends on SOC_CAMERA + depends on SOC_CAMERA && I2C select GPIO_PCA953X if MT9V022_PCA9536_SWITCH help This driver supports MT9V022 cameras from Micron diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index caf9085c2..29139b53d 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -87,16 +87,6 @@ obj-$(CONFIG_TUNER_3036) += tuner-3036.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o -obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o -obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o -# tuner-types will be merged into tuner-simple, in the future -obj-$(CONFIG_TUNER_SIMPLE) += tuner-types.o -obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o -obj-$(CONFIG_TUNER_TDA8290) += tda8290.o -obj-$(CONFIG_TUNER_TEA5767) += tea5767.o -obj-$(CONFIG_TUNER_TEA5761) += tea5761.o -obj-$(CONFIG_TUNER_TDA9887) += tda9887.o - obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o @@ -135,6 +125,7 @@ obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ +obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ @@ -144,5 +135,8 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o +obj-$(CONFIG_VIDEO_AU0828) += au0828/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/video/au0828/Kconfig b/linux/drivers/media/video/au0828/Kconfig new file mode 100644 index 000000000..52b249158 --- /dev/null +++ b/linux/drivers/media/video/au0828/Kconfig @@ -0,0 +1,13 @@ + +config VIDEO_AU0828 + tristate "Auvitek AU0828 support" + depends on I2C && INPUT && DVB_CORE && USB + select I2C_ALGOBIT + select VIDEO_TVEEPROM + select DVB_AU8522 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + ---help--- + This is a video4linux driver for Auvitek's USB device. + + To compile this driver as a module, choose M here: the + module will be called au0828 diff --git a/linux/drivers/media/video/au0828/Makefile b/linux/drivers/media/video/au0828/Makefile new file mode 100644 index 000000000..cd2c58281 --- /dev/null +++ b/linux/drivers/media/video/au0828/Makefile @@ -0,0 +1,9 @@ +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +obj-$(CONFIG_VIDEO_AU0828) += au0828.o + +EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/linux/drivers/media/video/au0828/au0828-cards.c b/linux/drivers/media/video/au0828/au0828-cards.c new file mode 100644 index 000000000..a2a698344 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-cards.c @@ -0,0 +1,181 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "au0828.h" +#include "au0828-cards.h" + +struct au0828_board au0828_boards[] = { + [AU0828_BOARD_UNKNOWN] = { + .name = "Unknown board", + }, + [AU0828_BOARD_HAUPPAUGE_HVR850] = { + .name = "Hauppauge HVR850", + }, + [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { + .name = "Hauppauge HVR950Q", + }, + [AU0828_BOARD_DVICO_FUSIONHDTV7] = { + .name = "DViCO FusionHDTV USB", + }, +}; + +/* Tuner callback function for au0828 boards. Currently only needed + * for HVR1500Q, which has an xc5000 tuner. + */ +int au0828_tuner_callback(void *priv, int command, int arg) +{ + struct au0828_dev *dev = priv; + + dprintk(1, "%s()\n", __func__); + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + if (command == 0) { + /* Tuner Reset Command from xc5000 */ + /* Drive the tuner into reset and out */ + au0828_clear(dev, REG_001, 2); + mdelay(200); + au0828_set(dev, REG_001, 2); + mdelay(50); + return 0; + } else { + printk(KERN_ERR + "%s(): Unknown command.\n", __func__); + return -EINVAL; + } + break; + } + + return 0; /* Should never be here */ +} + +static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) +{ + struct tveeprom tv; + + tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + + /* Make sure we support the board model */ + switch (tv.model) { + case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ + case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", __func__, tv.model); + break; + } + + printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + __func__, tv.model); +} + +void au0828_card_setup(struct au0828_dev *dev) +{ + static u8 eeprom[256]; + + dprintk(1, "%s()\n", __func__); + + if (dev->i2c_rc == 0) { + dev->i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); + } + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + if (dev->i2c_rc == 0) + hauppauge_eeprom(dev, eeprom+0xa0); + break; + } +} + +/* + * The bridge has between 8 and 12 gpios. + * Regs 1 and 0 deal with output enables. + * Regs 3 and 2 deal with direction. + */ +void au0828_gpio_setup(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + /* GPIO's + * 4 - CS5340 + * 5 - AU8522 Demodulator + * 6 - eeprom W/P + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_000, 0x88 | 0x20 | 0x40); + msleep(250); + break; + case AU0828_BOARD_DVICO_FUSIONHDTV7: + /* GPIO's + * 6 - ? + * 8 - AU8522 Demodulator + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_000, 0xa0); + msleep(250); + break; + } +} + +/* table of devices that work with this driver */ +struct usb_device_id au0828_usb_id_table [] = { + { USB_DEVICE(0x2040, 0x7200), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7240), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, + { USB_DEVICE(0x0fe9, 0xd620), + .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); diff --git a/linux/drivers/media/video/au0828/au0828-cards.h b/linux/drivers/media/video/au0828/au0828-cards.h new file mode 100644 index 000000000..e26f54a96 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-cards.h @@ -0,0 +1,25 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define AU0828_BOARD_UNKNOWN 0 +#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1 +#define AU0828_BOARD_HAUPPAUGE_HVR850 2 +#define AU0828_BOARD_DVICO_FUSIONHDTV7 3 diff --git a/linux/drivers/media/video/au0828/au0828-core.c b/linux/drivers/media/video/au0828/au0828-core.c new file mode 100644 index 000000000..5642058ae --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-core.c @@ -0,0 +1,262 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) +#include <linux/mutex.h> +#endif + +#include "au0828.h" + +/* + * 1 = General debug messages + * 2 = USB handling + * 4 = I2C related + * 8 = Bridge related + */ +int au0828_debug; +module_param_named(debug, au0828_debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +#define _AU0828_BULKPIPE 0x03 +#define _BULKPIPESIZE 0xffff + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); + +/* USB Direction */ +#define CMD_REQUEST_IN 0x00 +#define CMD_REQUEST_OUT 0x01 + +u32 au0828_readreg(struct au0828_dev *dev, u16 reg) +{ + recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1); + dprintk(8, "%s(0x%x) = 0x%x\n", __func__, reg, dev->ctrlmsg[0]); + return dev->ctrlmsg[0]; +} + +u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) +{ + dprintk(8, "%s(0x%x, 0x%x)\n", __func__, reg, val); + return send_control_msg(dev, CMD_REQUEST_OUT, val, reg, + dev->ctrlmsg, 0); +} + +static void cmd_msg_dump(struct au0828_dev *dev) +{ + int i; + + for (i = 0; i < sizeof(dev->ctrlmsg); i += 16) + dprintk(2, "%s() %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + __func__, + dev->ctrlmsg[i+0], dev->ctrlmsg[i+1], + dev->ctrlmsg[i+2], dev->ctrlmsg[i+3], + dev->ctrlmsg[i+4], dev->ctrlmsg[i+5], + dev->ctrlmsg[i+6], dev->ctrlmsg[i+7], + dev->ctrlmsg[i+8], dev->ctrlmsg[i+9], + dev->ctrlmsg[i+10], dev->ctrlmsg[i+11], + dev->ctrlmsg[i+12], dev->ctrlmsg[i+13], + dev->ctrlmsg[i+14], dev->ctrlmsg[i+15]); +} + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_sndctrlpipe(dev->usbdev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed sending control message, error %d.\n", + __func__, status); + } + + } + mutex_unlock(&dev->mutex); + return status; +} + +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + + memset(dev->ctrlmsg, 0, sizeof(dev->ctrlmsg)); + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_rcvctrlpipe(dev->usbdev, 0), + request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed receiving control message, error %d.\n", + __func__, status); + } else + cmd_msg_dump(dev); + } + mutex_unlock(&dev->mutex); + return status; +} + +static void au0828_usb_disconnect(struct usb_interface *interface) +{ + struct au0828_dev *dev = usb_get_intfdata(interface); + + dprintk(1, "%s()\n", __func__); + + /* Digital TV */ + au0828_dvb_unregister(dev); + + /* I2C */ + au0828_i2c_unregister(dev); + + usb_set_intfdata(interface, NULL); + + mutex_lock(&dev->mutex); + dev->usbdev = NULL; + mutex_unlock(&dev->mutex); + + kfree(dev); + +} + +static int au0828_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ifnum; + struct au0828_dev *dev; + struct usb_device *usbdev = interface_to_usbdev(interface); + + ifnum = interface->altsetting->desc.bInterfaceNumber; + + if (ifnum != 0) + return -ENODEV; + + dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__, + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct), + ifnum); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); + return -ENOMEM; + } + + mutex_init(&dev->mutex); + mutex_init(&dev->dvb.lock); + dev->usbdev = usbdev; + dev->board = id->driver_info; + + usb_set_intfdata(interface, dev); + + /* Power Up the bridge */ + au0828_write(dev, REG_600, 1 << 4); + + /* Bring up the GPIO's and supporting devices */ + au0828_gpio_setup(dev); + + /* I2C */ + au0828_i2c_register(dev); + + /* Setup */ + au0828_card_setup(dev); + + /* Digital TV */ + au0828_dvb_register(dev); + + printk(KERN_INFO "Registered device AU0828 [%s]\n", + au0828_boards[dev->board].name == NULL ? "Unset" : + au0828_boards[dev->board].name); + + return 0; +} + +static struct usb_driver au0828_usb_driver = { + .name = DRIVER_NAME, + .probe = au0828_usb_probe, + .disconnect = au0828_usb_disconnect, + .id_table = au0828_usb_id_table, +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) + .owner = THIS_MODULE, +#endif +}; + +static int __init au0828_init(void) +{ + int ret; + + if (au0828_debug & 1) + printk(KERN_INFO "%s() Debugging is enabled\n", __func__); + + if (au0828_debug & 2) + printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__); + + if (au0828_debug & 4) + printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__); + + if (au0828_debug & 8) + printk(KERN_INFO "%s() Bridge Debugging is enabled\n", + __func__); + + printk(KERN_INFO "au0828 driver loaded\n"); + + ret = usb_register(&au0828_usb_driver); + if (ret) + printk(KERN_ERR "usb_register failed, error = %d\n", ret); + + return ret; +} + +static void __exit au0828_exit(void) +{ + usb_deregister(&au0828_usb_driver); +} + +module_init(au0828_init); +module_exit(au0828_exit); + +MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); +MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c new file mode 100644 index 000000000..709a703fe --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -0,0 +1,393 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/suspend.h> +#include <media/v4l2-common.h> +#include "compat.h" + +#include "au0828.h" +#include "au8522.h" +#include "xc5000.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define _AU0828_BULKPIPE 0x83 +#define _BULKPIPESIZE 0xe522 + +static struct au8522_config hauppauge_hvr950q_config = { + .demod_address = 0x8e >> 1, + .status_mode = AU8522_DEMODLOCKING, +}; + +static struct xc5000_config hauppauge_hvr950q_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 6000, + .tuner_callback = au0828_tuner_callback +}; + +/*-------------------------------------------------------------------*/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void urb_completion(struct urb *purb, struct pt_regs *regs) +#else +static void urb_completion(struct urb *purb) +#endif +{ + u8 *ptr; + struct au0828_dev *dev = purb->context; + int ptype = usb_pipetype(purb->pipe); + + dprintk(2, "%s()\n", __func__); + + if (!dev) + return; + + if (dev->urb_streaming == 0) + return; + + if (ptype != PIPE_BULK) { + printk(KERN_ERR "%s() Unsupported URB type %d\n", + __func__, ptype); + return; + } + + ptr = (u8 *)purb->transfer_buffer; + + /* Feed the transport payload into the kernel demux */ + dvb_dmx_swfilter_packets(&dev->dvb.demux, + purb->transfer_buffer, purb->actual_length / 188); + + /* Clean the buffer before we requeue */ + memset(purb->transfer_buffer, 0, URB_BUFSIZE); + + /* Requeue URB */ + usb_submit_urb(purb, GFP_ATOMIC); +} + +static int stop_urb_transfer(struct au0828_dev *dev) +{ + int i; + + dprintk(2, "%s()\n", __func__); + + for (i = 0; i < URB_COUNT; i++) { + usb_kill_urb(dev->urbs[i]); + kfree(dev->urbs[i]->transfer_buffer); + usb_free_urb(dev->urbs[i]); + } + + dev->urb_streaming = 0; + + return 0; +} + +static int start_urb_transfer(struct au0828_dev *dev) +{ + struct urb *purb; + int i, ret = -ENOMEM; + + dprintk(2, "%s()\n", __func__); + + if (dev->urb_streaming) { + dprintk(2, "%s: iso xfer already running!\n", __func__); + return 0; + } + + for (i = 0; i < URB_COUNT; i++) { + + dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urbs[i]) + goto err; + + purb = dev->urbs[i]; + + purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); + if (!purb->transfer_buffer) { + usb_free_urb(purb); + dev->urbs[i] = NULL; + goto err; + } + + purb->status = -EINPROGRESS; + usb_fill_bulk_urb(purb, + dev->usbdev, + usb_rcvbulkpipe(dev->usbdev, _AU0828_BULKPIPE), + purb->transfer_buffer, + URB_BUFSIZE, + urb_completion, + dev); + + } + + for (i = 0; i < URB_COUNT; i++) { + ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); + if (ret != 0) { + stop_urb_transfer(dev); + printk(KERN_ERR "%s: failed urb submission, " + "err = %d\n", __func__, ret); + return ret; + } + } + + dev->urb_streaming = 1; + ret = 0; + +err: + return ret; +} + +static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + ret = start_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + au0828_write(dev, 0x608, 0x00); + au0828_write(dev, 0x609, 0x00); + au0828_write(dev, 0x60a, 0x00); + au0828_write(dev, 0x60b, 0x00); + ret = stop_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +static int dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int result; + + dprintk(1, "%s()\n", __func__); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + &dev->usbdev->dev, adapter_nr); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_adapter; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); +#else + dvb->adapter->priv = dev; + + /* register frontend */ + result = dvb_register_frontend(dvb->adapter, dvb->frontend); +#endif + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_frontend failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = au0828_dvb_start_feed; + dvb->demux.stop_feed = au0828_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); +#else + result = dvb_dmxdev_init(&dvb->dmxdev, dvb->adapter); +#endif + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_fe_conn; + } + + /* register network adapter */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); +#else + dvb_net_init(dvb->adapter, &dvb->net, &dvb->demux.dmx); +#endif + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +fail_adapter: + return result; +} + +void au0828_dvb_unregister(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + + dprintk(1, "%s()\n", __func__); + + if (dvb->frontend == NULL) + return; + + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +} + +/* All the DVB attach calls go here, this function get's modified + * for each new card. No other function in this file needs + * to change. + */ +int au0828_dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int ret; + + dprintk(1, "%s()\n", __func__); + + /* init frontend */ + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_hvr950q_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_hvr950q_tunerconfig, dev); + break; + default: + printk(KERN_WARNING "The frontend of your DVB/ATSC card " + "isn't supported yet\n"); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR "%s() Frontend initialization failed\n", + __func__); + return -1; + } + + /* register everything */ + ret = dvb_register(dev); + if (ret < 0) { + if (dvb->frontend->ops.release) + dvb->frontend->ops.release(dvb->frontend); + return ret; + } + + return 0; +} diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c new file mode 100644 index 000000000..b40a086e4 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -0,0 +1,384 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "compat.h" +#include "au0828.h" + +#include <media/v4l2-common.h> + +static int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +#define I2C_WAIT_DELAY 512 +#define I2C_WAIT_RETRY 64 + +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; +} + +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; +} + +static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_slave_did_read_ack(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +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; +} + +static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_read_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +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; +} + +static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (i2c_is_write_done(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +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; +} + +static int i2c_wait_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_sendbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined_rlen) +{ + int i, strobe = 0; + struct au0828_dev *dev = i2c_adap->algo_data; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, REG_2FF, 0x01); + au0828_write(dev, REG_202, 0x07); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, REG_203, msg->addr << 1); + + dprintk(4, "SEND: %02x\n", msg->addr); + + for (i = 0; i < msg->len;) { + + dprintk(4, " %02x\n", msg->buf[i]); + + au0828_write(dev, REG_205, msg->buf[i]); + + strobe++; + i++; + + if ((strobe >= 4) || (i >= msg->len)) { + + /* Strobe the byte into the bus */ + if (i < msg->len) + au0828_write(dev, REG_200, 0x41); + else + au0828_write(dev, REG_200, 0x01); + + /* Reset strobe trigger */ + strobe = 0; + + if (!i2c_wait_write_done(i2c_adap)) + return -EIO; + + } + + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_readbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + int i; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, REG_2FF, 0x01); + au0828_write(dev, REG_202, 0x07); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, REG_203, msg->addr << 1); + + dprintk(4, " RECV:\n"); + + /* Deal with i2c_scan */ + if (msg->len == 0) { + au0828_write(dev, REG_200, 0x20); + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + return 0; + } + + for (i = 0; i < msg->len;) { + + i++; + + if (i < msg->len) + au0828_write(dev, REG_200, 0x60); + else + au0828_write(dev, REG_200, 0x20); + + if (!i2c_wait_read_done(i2c_adap)) + return -EIO; + + msg->buf[i-1] = au0828_read(dev, REG_209) & 0xff; + + dprintk(4, " %02x\n", msg->buf[i-1]); + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +static int i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + int i, retval = 0; + + dprintk(4, "%s(num = %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = i2c_readbytes(i2c_adap, &msgs[i], 0); + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], + msgs[i + 1].len); + if (retval < 0) + goto err; + i++; + retval = i2c_readbytes(i2c_adap, &msgs[i], 1); + } else { + /* write */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); + } + if (retval < 0) + goto err; + } + return num; + +err: + return retval; +} + +static int attach_inform(struct i2c_client *client) +{ + dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", + client->driver->driver.name, client->addr, client->name); + + if (!client->driver->command) + return 0; + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + dprintk(1, "i2c detach [client=%s]\n", client->name); + + return 0; +} + +void au0828_call_i2c_clients(struct au0828_dev *dev, + unsigned int cmd, void *arg) +{ + if (dev->i2c_rc != 0) + return; + + i2c_clients_command(&dev->i2c_adap, cmd, arg); +} + +static u32 au0828_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm au0828_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = au0828_functionality, +}; + +/* ----------------------------------------------------------------------- */ + +static struct i2c_adapter au0828_i2c_adap_template = { + .name = DRIVER_NAME, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + .owner = THIS_MODULE, +#endif + .id = I2C_HW_B_AU0828, + .algo = &au0828_i2c_algo_template, + .class = I2C_CLASS_TV_ANALOG, + .client_register = attach_inform, + .client_unregister = detach_inform, +}; + +static struct i2c_client au0828_i2c_client_template = { + .name = "au0828 internal", +}; + +static char *i2c_devs[128] = { + [0x8e >> 1] = "au8522", + [0xa0 >> 1] = "eeprom", + [0xc2 >> 1] = "tuner/xc5000", +}; + +static void do_i2c_scan(char *name, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", + name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + } +} + +/* init + register i2c algo-bit adapter */ +int au0828_i2c_register(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, + sizeof(dev->i2c_adap)); + memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, + sizeof(dev->i2c_algo)); + memcpy(&dev->i2c_client, &au0828_i2c_client_template, + sizeof(dev->i2c_client)); + + dev->i2c_adap.dev.parent = &dev->usbdev->dev; + + strlcpy(dev->i2c_adap.name, DRIVER_NAME, + sizeof(dev->i2c_adap.name)); + + dev->i2c_algo.data = dev; + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, dev); + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client.adapter = &dev->i2c_adap; + + if (0 == dev->i2c_rc) { + printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME); + if (i2c_scan) + do_i2c_scan(DRIVER_NAME, &dev->i2c_client); + } else + printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME); + + return dev->i2c_rc; +} + +int au0828_i2c_unregister(struct au0828_dev *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} + diff --git a/linux/drivers/media/video/au0828/au0828-reg.h b/linux/drivers/media/video/au0828/au0828-reg.h new file mode 100644 index 000000000..398275508 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-reg.h @@ -0,0 +1,38 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* We'll start to rename these registers once we have a better + * understanding of their meaning. + */ +#define REG_000 0x000 +#define REG_001 0x001 +#define REG_002 0x002 +#define REG_003 0x003 + +#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 + +#define REG_600 0x600 diff --git a/linux/drivers/media/video/au0828/au0828.h b/linux/drivers/media/video/au0828/au0828.h new file mode 100644 index 000000000..b00d8cda5 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828.h @@ -0,0 +1,132 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <media/tveeprom.h> + +/* DVB */ +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" + +#include "au0828-reg.h" +#include "au0828-cards.h" + +#define DRIVER_NAME "au0828" +#define URB_COUNT 16 +#define URB_BUFSIZE (0xe522) + +struct au0828_board { + char *name; +}; + +struct au0828_dvb { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex lock; +#else + struct semaphore lock; +#endif + struct dvb_adapter adapter; + struct dvb_frontend *frontend; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; + int feeding; +}; + +struct au0828_dev { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex mutex; +#else + struct semaphore mutex; +#endif + struct usb_device *usbdev; + int board; + u8 ctrlmsg[64]; + + /* I2C */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* Digital */ + struct au0828_dvb dvb; + + /* USB / URB Related */ + int urb_streaming; + struct urb *urbs[URB_COUNT]; + +}; + +struct au0828_buff { + struct au0828_dev *dev; + struct urb *purb; + struct list_head buff_list; +}; + +/* ----------------------------------------------------------- */ +#define au0828_read(dev, reg) au0828_readreg(dev, reg) +#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value) +#define au0828_andor(dev, reg, mask, value) \ + au0828_writereg(dev, reg, \ + (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask))) + +#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit)) +#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0) + +/* ----------------------------------------------------------- */ +/* au0828-core.c */ +extern u32 au0828_read(struct au0828_dev *dev, u16 reg); +extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val); +extern int au0828_debug; + +/* ----------------------------------------------------------- */ +/* au0828-cards.c */ +extern struct au0828_board au0828_boards[]; +extern struct usb_device_id au0828_usb_id_table[]; +extern void au0828_gpio_setup(struct au0828_dev *dev); +extern int au0828_tuner_callback(void *priv, int command, int arg); +extern void au0828_card_setup(struct au0828_dev *dev); + +/* ----------------------------------------------------------- */ +/* au0828-i2c.c */ +extern int au0828_i2c_register(struct au0828_dev *dev); +extern int au0828_i2c_unregister(struct au0828_dev *dev); +extern void au0828_call_i2c_clients(struct au0828_dev *dev, + unsigned int cmd, void *arg); + +/* ----------------------------------------------------------- */ +/* au0828-dvb.c */ +extern int au0828_dvb_register(struct au0828_dev *dev); +extern void au0828_dvb_unregister(struct au0828_dev *dev); + +#define dprintk(level, fmt, arg...)\ + do { if (au0828_debug & level)\ + printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ + } while (0) diff --git a/linux/drivers/media/video/bt8xx/Kconfig b/linux/drivers/media/video/bt8xx/Kconfig index cfc822bb5..24a34fc1f 100644 --- a/linux/drivers/media/video/bt8xx/Kconfig +++ b/linux/drivers/media/video/bt8xx/Kconfig @@ -1,6 +1,7 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 && INPUT + depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT select FW_LOADER select VIDEO_BTCX diff --git a/linux/drivers/media/video/bt8xx/Makefile b/linux/drivers/media/video/bt8xx/Makefile index 924d216d9..e415f6fc4 100644 --- a/linux/drivers/media/video/bt8xx/Makefile +++ b/linux/drivers/media/video/bt8xx/Makefile @@ -9,4 +9,5 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ obj-$(CONFIG_VIDEO_BT848) += bttv.o EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 303422115..a836e0ed4 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -2654,7 +2654,7 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) struct bttv_fh *fh = priv; mutex_lock(&fh->cap.vb_lock); - retval = videobuf_mmap_setup(&fh->cap, gbuffers, gbufsize, + retval = __videobuf_mmap_setup(&fh->cap, gbuffers, gbufsize, V4L2_MEMORY_MMAP); if (retval < 0) { mutex_unlock(&fh->cap.vb_lock); diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index e9219458e..b8ab81fb8 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -90,8 +90,6 @@ /* Limits scaled width, which must be a multiple of 4. */ #define MAX_HACTIVE (0x3FF & -4) -#define clamp(x, low, high) min (max (low, x), high) - #define BTTV_NORMS (\ V4L2_STD_PAL | V4L2_STD_PAL_N | \ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \ diff --git a/linux/drivers/media/video/compat_ioctl32.c b/linux/drivers/media/video/compat_ioctl32.c index c1ca880d0..6b405e75c 100644 --- a/linux/drivers/media/video/compat_ioctl32.c +++ b/linux/drivers/media/video/compat_ioctl32.c @@ -950,6 +950,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: case VIDIOC_TRY_FMT32: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/linux/drivers/media/video/cs5345.c b/linux/drivers/media/video/cs5345.c index 0377a24c6..d7bcb6d9e 100644 --- a/linux/drivers/media/video/cs5345.c +++ b/linux/drivers/media/video/cs5345.c @@ -156,7 +156,8 @@ static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) /* ----------------------------------------------------------------------- */ -static int cs5345_probe(struct i2c_client *client) +static int cs5345_probe(struct i2c_client *client, + const struct i2c_device_id *id) { /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c index fe92723f1..e98574f42 100644 --- a/linux/drivers/media/video/cs53l32a.c +++ b/linux/drivers/media/video/cs53l32a.c @@ -147,7 +147,8 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int cs53l32a_probe(struct i2c_client *client) +static int cs53l32a_probe(struct i2c_client *client, + const struct i2c_device_id *id) { int i; diff --git a/linux/drivers/media/video/cx18/Kconfig b/linux/drivers/media/video/cx18/Kconfig new file mode 100644 index 000000000..5f9426905 --- /dev/null +++ b/linux/drivers/media/video/cx18/Kconfig @@ -0,0 +1,23 @@ +config VIDEO_CX18 + tristate "Conexant cx23418 MPEG encoder support" + depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL + depends on INPUT # due to VIDEO_IR + depends on HOTPLUG # due to FW_LOADER + select I2C_ALGOBIT + select FW_LOADER + select VIDEO_IR + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_CX2341X + select VIDEO_CS5345 + select DVB_S5H1409 + select MEDIA_TUNER_MXL5005S + ---help--- + This is a video4linux driver for Conexant cx23418 based + PCI combo video recorder devices. + + This is used in devices such as the Hauppauge HVR-1600 + cards. + + To compile this driver as a module, choose M here: the + module will be called cx18. diff --git a/linux/drivers/media/video/cx18/Makefile b/linux/drivers/media/video/cx18/Makefile new file mode 100644 index 000000000..b23d2e261 --- /dev/null +++ b/linux/drivers/media/video/cx18/Makefile @@ -0,0 +1,11 @@ +cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \ + cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ + cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ + cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ + cx18-dvb.o + +obj-$(CONFIG_VIDEO_CX18) += cx18.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/video/cx18/cx18-audio.c b/linux/drivers/media/video/cx18/cx18-audio.c new file mode 100644 index 000000000..1adc404d9 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-audio.c @@ -0,0 +1,73 @@ +/* + * cx18 audio-related functions + * + * Derived from ivtv-audio.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-i2c.h" +#include "cx18-cards.h" +#include "cx18-audio.h" + +/* Selects the audio input and output according to the current + settings. */ +int cx18_audio_set_io(struct cx18 *cx) +{ + struct v4l2_routing route; + u32 audio_input; + int mux_input; + + /* Determine which input to use */ + if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { + audio_input = cx->card->radio_input.audio_input; + mux_input = cx->card->radio_input.muxer_input; + } else { + audio_input = + cx->card->audio_inputs[cx->audio_input].audio_input; + mux_input = + cx->card->audio_inputs[cx->audio_input].muxer_input; + } + + /* handle muxer chips */ + route.input = mux_input; + route.output = 0; + cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + + route.input = audio_input; + return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, + VIDIOC_INT_S_AUDIO_ROUTING, &route); +} + +void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route) +{ + cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, + VIDIOC_INT_S_AUDIO_ROUTING, route); +} + +void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq) +{ + static u32 freqs[3] = { 44100, 48000, 32000 }; + + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (freq > 2) + return; + cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); +} diff --git a/linux/drivers/media/video/cx18/cx18-audio.h b/linux/drivers/media/video/cx18/cx18-audio.h new file mode 100644 index 000000000..cb569a693 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-audio.h @@ -0,0 +1,26 @@ +/* + * cx18 audio-related functions + * + * Derived from ivtv-audio.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int cx18_audio_set_io(struct cx18 *cx); +void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route); +void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq); diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c new file mode 100644 index 000000000..2dc3a5dd1 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-av-audio.c @@ -0,0 +1,361 @@ +/* + * cx18 ADEC audio functions + * + * Derived from cx25840-audio.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "cx18-driver.h" + +static int set_audclk_freq(struct cx18 *cx, u32 freq) +{ + struct cx18_av_state *state = &cx->av_state; + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + /* common for all inputs and rates */ + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ + cx18_av_write(cx, 0x127, 0x50); + + if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* VID_PLL and AUX_PLL */ + cx18_av_write4(cx, 0x108, 0x1006040f); + + /* AUX_PLL_FRAC */ + cx18_av_write4(cx, 0x110, 0x01bb39ee); + + /* src3/4/6_ctl = 0x0801f77f */ + cx18_av_write4(cx, 0x900, 0x0801f77f); + cx18_av_write4(cx, 0x904, 0x0801f77f); + cx18_av_write4(cx, 0x90c, 0x0801f77f); + break; + + case 44100: + /* VID_PLL and AUX_PLL */ + cx18_av_write4(cx, 0x108, 0x1009040f); + + /* AUX_PLL_FRAC */ + cx18_av_write4(cx, 0x110, 0x00ec6bd6); + + /* src3/4/6_ctl = 0x08016d59 */ + cx18_av_write4(cx, 0x900, 0x08016d59); + cx18_av_write4(cx, 0x904, 0x08016d59); + cx18_av_write4(cx, 0x90c, 0x08016d59); + break; + + case 48000: + /* VID_PLL and AUX_PLL */ + cx18_av_write4(cx, 0x108, 0x100a040f); + + /* AUX_PLL_FRAC */ + cx18_av_write4(cx, 0x110, 0x0098d6e5); + + /* src3/4/6_ctl = 0x08014faa */ + cx18_av_write4(cx, 0x900, 0x08014faa); + cx18_av_write4(cx, 0x904, 0x08014faa); + cx18_av_write4(cx, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + case 32000: + /* VID_PLL and AUX_PLL */ + cx18_av_write4(cx, 0x108, 0x1e08040f); + + /* AUX_PLL_FRAC */ + cx18_av_write4(cx, 0x110, 0x012a0869); + + /* src1_ctl = 0x08010000 */ + cx18_av_write4(cx, 0x8f8, 0x08010000); + + /* src3/4/6_ctl = 0x08020000 */ + cx18_av_write4(cx, 0x900, 0x08020000); + cx18_av_write4(cx, 0x904, 0x08020000); + cx18_av_write4(cx, 0x90c, 0x08020000); + + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ + cx18_av_write(cx, 0x127, 0x54); + break; + + case 44100: + /* VID_PLL and AUX_PLL */ + cx18_av_write4(cx, 0x108, 0x1809040f); + + /* AUX_PLL_FRAC */ + cx18_av_write4(cx, 0x110, 0x00ec6bd6); + + /* src1_ctl = 0x08010000 */ + cx18_av_write4(cx, 0x8f8, 0x080160cd); + + /* src3/4/6_ctl = 0x08020000 */ + cx18_av_write4(cx, 0x900, 0x08017385); + cx18_av_write4(cx, 0x904, 0x08017385); + cx18_av_write4(cx, 0x90c, 0x08017385); + break; + + case 48000: + /* VID_PLL and AUX_PLL */ + cx18_av_write4(cx, 0x108, 0x180a040f); + + /* AUX_PLL_FRAC */ + cx18_av_write4(cx, 0x110, 0x0098d6e5); + + /* src1_ctl = 0x08010000 */ + cx18_av_write4(cx, 0x8f8, 0x08018000); + + /* src3/4/6_ctl = 0x08020000 */ + cx18_av_write4(cx, 0x900, 0x08015555); + cx18_av_write4(cx, 0x904, 0x08015555); + cx18_av_write4(cx, 0x90c, 0x08015555); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +void cx18_av_audio_set_path(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + + /* stop microcontroller */ + cx18_av_and_or(cx, 0x803, ~0x10, 0); + + /* assert soft reset */ + cx18_av_and_or(cx, 0x810, ~0x1, 0x01); + + /* Mute everything to prevent the PFFT! */ + cx18_av_write(cx, 0x8d3, 0x1f); + + if (state->aud_input == CX18_AV_AUDIO_SERIAL) { + /* Set Path1 to Serial Audio Input */ + cx18_av_write4(cx, 0x8d0, 0x01011012); + + /* The microcontroller should not be started for the + * non-tuner inputs: autodetection is specific for + * TV audio. */ + } else { + /* Set Path1 to Analog Demod Main Channel */ + cx18_av_write4(cx, 0x8d0, 0x1f063870); + } + + set_audclk_freq(cx, state->audclk_freq); + + /* deassert soft reset */ + cx18_av_and_or(cx, 0x810, ~0x1, 0x00); + + if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + /* When the microcontroller detects the + * audio format, it will unmute the lines */ + cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + } +} + +static int get_volume(struct cx18 *cx) +{ + /* Volume runs +18dB to -96dB in 1/2dB steps + * change to fit the msp3400 -114dB to +12dB range */ + + /* check PATH1_VOLUME */ + int vol = 228 - cx18_av_read(cx, 0x8d4); + vol = (vol / 2) + 23; + return vol << 9; +} + +static void set_volume(struct cx18 *cx, int volume) +{ + /* First convert the volume to msp3400 values (0-127) */ + int vol = volume >> 9; + /* now scale it up to cx18_av values + * -114dB to -96dB maps to 0 + * this should be 19, but in my testing that was 4dB too loud */ + if (vol <= 23) + vol = 0; + else + vol -= 23; + + /* PATH1_VOLUME */ + cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); +} + +static int get_bass(struct cx18 *cx) +{ + /* bass is 49 steps +12dB to -12dB */ + + /* check PATH1_EQ_BASS_VOL */ + int bass = cx18_av_read(cx, 0x8d9) & 0x3f; + bass = (((48 - bass) * 0xffff) + 47) / 48; + return bass; +} + +static void set_bass(struct cx18 *cx, int bass) +{ + /* PATH1_EQ_BASS_VOL */ + cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); +} + +static int get_treble(struct cx18 *cx) +{ + /* treble is 49 steps +12dB to -12dB */ + + /* check PATH1_EQ_TREBLE_VOL */ + int treble = cx18_av_read(cx, 0x8db) & 0x3f; + treble = (((48 - treble) * 0xffff) + 47) / 48; + return treble; +} + +static void set_treble(struct cx18 *cx, int treble) +{ + /* PATH1_EQ_TREBLE_VOL */ + cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); +} + +static int get_balance(struct cx18 *cx) +{ + /* balance is 7 bit, 0 to -96dB */ + + /* check PATH1_BAL_LEVEL */ + int balance = cx18_av_read(cx, 0x8d5) & 0x7f; + /* check PATH1_BAL_LEFT */ + if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0) + balance = 0x80 - balance; + else + balance = 0x80 + balance; + return balance << 8; +} + +static void set_balance(struct cx18 *cx, int balance) +{ + int bal = balance >> 8; + if (bal > 0x80) { + /* PATH1_BAL_LEFT */ + cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80); + /* PATH1_BAL_LEVEL */ + cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f); + } else { + /* PATH1_BAL_LEFT */ + cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00); + /* PATH1_BAL_LEVEL */ + cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal); + } +} + +static int get_mute(struct cx18 *cx) +{ + /* check SRC1_MUTE_EN */ + return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0; +} + +static void set_mute(struct cx18 *cx, int mute) +{ + struct cx18_av_state *state = &cx->av_state; + + if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + /* Must turn off microcontroller in order to mute sound. + * Not sure if this is the best method, but it does work. + * If the microcontroller is running, then it will undo any + * changes to the mute register. */ + if (mute) { + /* disable microcontroller */ + cx18_av_and_or(cx, 0x803, ~0x10, 0x00); + cx18_av_write(cx, 0x8d3, 0x1f); + } else { + /* enable microcontroller */ + cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + } + } else { + /* SRC1_MUTE_EN */ + cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00); + } +} + +int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_control *ctrl = arg; + int retval; + + switch (cmd) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + cx18_av_and_or(cx, 0x803, ~0x10, 0); + cx18_av_write(cx, 0x8d3, 0x1f); + } + cx18_av_and_or(cx, 0x810, ~0x1, 1); + retval = set_audclk_freq(cx, *(u32 *)arg); + cx18_av_and_or(cx, 0x810, ~0x1, 0); + if (state->aud_input != CX18_AV_AUDIO_SERIAL) + cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + return retval; + + case VIDIOC_G_CTRL: + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = get_volume(cx); + break; + case V4L2_CID_AUDIO_BASS: + ctrl->value = get_bass(cx); + break; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = get_treble(cx); + break; + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = get_balance(cx); + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(cx); + break; + default: + return -EINVAL; + } + break; + + case VIDIOC_S_CTRL: + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + set_volume(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_BASS: + set_bass(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_TREBLE: + set_treble(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_MUTE: + set_mute(cx, ctrl->value); + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c new file mode 100644 index 000000000..9a2675161 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -0,0 +1,888 @@ +/* + * cx18 ADEC audio functions + * + * Derived from cx25840-core.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "cx18-driver.h" + +int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) +{ + u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); + u32 mask = 0xff; + int shift = (addr & 3) * 8; + + x = (x & ~(mask << shift)) | ((u32)value << shift); + writel(x, cx->reg_mem + 0xc40000 + (addr & ~3)); + return 0; +} + +int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) +{ + writel(value, cx->reg_mem + 0xc40000 + addr); + return 0; +} + +u8 cx18_av_read(struct cx18 *cx, u16 addr) +{ + u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); + int shift = (addr & 3) * 8; + + return (x >> shift) & 0xff; +} + +u32 cx18_av_read4(struct cx18 *cx, u16 addr) +{ + return readl(cx->reg_mem + 0xc40000 + addr); +} + +int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, + u8 or_value) +{ + return cx18_av_write(cx, addr, + (cx18_av_read(cx, addr) & and_mask) | + or_value); +} + +int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, + u32 or_value) +{ + return cx18_av_write4(cx, addr, + (cx18_av_read4(cx, addr) & and_mask) | + or_value); +} + +/* ----------------------------------------------------------------------- */ + +static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, + enum cx18_av_audio_input aud_input); +static void log_audio_status(struct cx18 *cx); +static void log_video_status(struct cx18 *cx); + +/* ----------------------------------------------------------------------- */ + +static void cx18_av_initialize(struct cx18 *cx) +{ + u32 v; + + cx18_av_loadfw(cx); + /* Stop 8051 code execution */ + cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000); + + /* initallize the PLL by toggling sleep bit */ + v = cx18_av_read4(cx, CXADEC_HOST_REG1); + /* enable sleep mode */ + cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1); + /* disable sleep mode */ + cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe); + + /* initialize DLLs */ + v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; + /* disable FLD */ + cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v); + /* enable FLD */ + cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100); + + v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF; + /* disable FLD */ + cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v); + /* enable FLD */ + cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100); + + /* set analog bias currents. Set Vreg to 1.20V. */ + cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802); + + v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; + /* enable TUNE_FIL_RST */ + cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v); + /* disable TUNE_FIL_RST */ + cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE); + + /* enable 656 output */ + cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); + + /* video output drive strength */ + cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2); + + /* reset video */ + cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); + cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); + + /* set video to auto-detect */ + /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ + /* set the comb notch = 1 */ + cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800); + + /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */ + /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */ + cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000); + + /* Set VGA_TRACK_RANGE to 0x20 */ + cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); + + /* Enable VBI capture */ + cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F); + /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */ + + /* Set the video input. + The setting in MODE_CTRL gets lost when we do the above setup */ + /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ + /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ + + v = cx18_av_read4(cx, CXADEC_AFE_CTRL); + v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */ + v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */ + v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */ + /* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */ + cx18_av_write4(cx, CXADEC_AFE_CTRL, v); + +/* if(dwEnable && dw3DCombAvailable) { */ +/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ +/* } else { */ +/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ +/* } */ + cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); +} + +/* ----------------------------------------------------------------------- */ + +static void input_change(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + v4l2_std_id std = state->std; + + /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ + if (std & V4L2_STD_SECAM) + cx18_av_write(cx, 0x402, 0); + else { + cx18_av_write(cx, 0x402, 0x04); + cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); + } + cx18_av_and_or(cx, 0x401, ~0x60, 0); + cx18_av_and_or(cx, 0x401, ~0x60, 0x60); + + if (std & V4L2_STD_525_60) { + if (std == V4L2_STD_NTSC_M_JP) { + /* Japan uses EIAJ audio standard */ + cx18_av_write(cx, 0x808, 0xf7); + cx18_av_write(cx, 0x80b, 0x02); + } else if (std == V4L2_STD_NTSC_M_KR) { + /* South Korea uses A2 audio standard */ + cx18_av_write(cx, 0x808, 0xf8); + cx18_av_write(cx, 0x80b, 0x03); + } else { + /* Others use the BTSC audio standard */ + cx18_av_write(cx, 0x808, 0xf6); + cx18_av_write(cx, 0x80b, 0x01); + } + } else if (std & V4L2_STD_PAL) { + /* Follow tuner change procedure for PAL */ + cx18_av_write(cx, 0x808, 0xff); + cx18_av_write(cx, 0x80b, 0x03); + } else if (std & V4L2_STD_SECAM) { + /* Select autodetect for SECAM */ + cx18_av_write(cx, 0x808, 0xff); + cx18_av_write(cx, 0x80b, 0x03); + } + + if (cx18_av_read(cx, 0x803) & 0x10) { + /* restart audio decoder microcontroller */ + cx18_av_and_or(cx, 0x803, ~0x10, 0x00); + cx18_av_and_or(cx, 0x803, ~0x10, 0x10); + } +} + +static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, + enum cx18_av_audio_input aud_input) +{ + struct cx18_av_state *state = &cx->av_state; + u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && + vid_input <= CX18_AV_COMPOSITE8); + u8 reg; + + CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n", + vid_input, aud_input); + + if (is_composite) { + reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); + } else { + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; + + if ((vid_input & ~0xff0) || + luma < CX18_AV_SVIDEO_LUMA1 || + luma > CX18_AV_SVIDEO_LUMA4 || + chroma < CX18_AV_SVIDEO_CHROMA4 || + chroma > CX18_AV_SVIDEO_CHROMA8) { + CX18_ERR("0x%04x is not a valid video input!\n", + vid_input); + return -EINVAL; + } + reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); + if (chroma >= CX18_AV_SVIDEO_CHROMA7) { + reg &= 0x3f; + reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; + } else { + reg &= 0xcf; + reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; + } + } + + switch (aud_input) { + case CX18_AV_AUDIO_SERIAL: + /* do nothing, use serial audio input */ + break; + case CX18_AV_AUDIO4: reg &= ~0x30; break; + case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break; + case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break; + case CX18_AV_AUDIO7: reg &= ~0xc0; break; + case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; + + default: + CX18_ERR("0x%04x is not a valid audio input!\n", aud_input); + return -EINVAL; + } + + cx18_av_write(cx, 0x103, reg); + /* Set INPUT_MODE to Composite (0) or S-Video (1) */ + cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); + /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ + cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); + /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ + if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) + cx18_av_and_or(cx, 0x102, ~0x4, 4); + else + cx18_av_and_or(cx, 0x102, ~0x4, 0); + /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/ + + state->vid_input = vid_input; + state->aud_input = aud_input; + cx18_av_audio_set_path(cx); + input_change(cx); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int set_v4lstd(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + u8 fmt = 0; /* zero is autodetect */ + u8 pal_m = 0; + + /* First tests should be against specific std */ + if (state->std == V4L2_STD_NTSC_M_JP) { + fmt = 0x2; + } else if (state->std == V4L2_STD_NTSC_443) { + fmt = 0x3; + } else if (state->std == V4L2_STD_PAL_M) { + pal_m = 1; + fmt = 0x5; + } else if (state->std == V4L2_STD_PAL_N) { + fmt = 0x6; + } else if (state->std == V4L2_STD_PAL_Nc) { + fmt = 0x7; + } else if (state->std == V4L2_STD_PAL_60) { + fmt = 0x8; + } else { + /* Then, test against generic ones */ + if (state->std & V4L2_STD_NTSC) + fmt = 0x1; + else if (state->std & V4L2_STD_PAL) + fmt = 0x4; + else if (state->std & V4L2_STD_SECAM) + fmt = 0xc; + } + + CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt); + + /* Follow step 9 of section 3.16 in the cx18_av datasheet. + Without this PAL may display a vertical ghosting effect. + This happens for example with the Yuan MPC622. */ + if (fmt >= 4 && fmt < 8) { + /* Set format to NTSC-M */ + cx18_av_and_or(cx, 0x400, ~0xf, 1); + /* Turn off LCOMB */ + cx18_av_and_or(cx, 0x47b, ~6, 0); + } + cx18_av_and_or(cx, 0x400, ~0xf, fmt); + cx18_av_and_or(cx, 0x403, ~0x3, pal_m); + cx18_av_vbi_setup(cx); + input_change(cx); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + CX18_ERR("invalid brightness setting %d\n", + ctrl->value); + return -ERANGE; + } + + cx18_av_write(cx, 0x414, ctrl->value - 128); + break; + + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 127) { + CX18_ERR("invalid contrast setting %d\n", + ctrl->value); + return -ERANGE; + } + + cx18_av_write(cx, 0x415, ctrl->value << 1); + break; + + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 127) { + CX18_ERR("invalid saturation setting %d\n", + ctrl->value); + return -ERANGE; + } + + cx18_av_write(cx, 0x420, ctrl->value << 1); + cx18_av_write(cx, 0x421, ctrl->value << 1); + break; + + case V4L2_CID_HUE: + if (ctrl->value < -127 || ctrl->value > 127) { + CX18_ERR("invalid hue setting %d\n", ctrl->value); + return -ERANGE; + } + + cx18_av_write(cx, 0x422, ctrl->value); + break; + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_MUTE: + return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl); + + default: + return -EINVAL; + } + + return 0; +} + +static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; + break; + case V4L2_CID_CONTRAST: + ctrl->value = cx18_av_read(cx, 0x415) >> 1; + break; + case V4L2_CID_SATURATION: + ctrl->value = cx18_av_read(cx, 0x420) >> 1; + break; + case V4L2_CID_HUE: + ctrl->value = (s8)cx18_av_read(cx, 0x422); + break; + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_MUTE: + return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl); + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) +{ + switch (fmt->type) { + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); + default: + return -EINVAL; + } + + return 0; +} + +static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_pix_format *pix; + int HSC, VSC, Vsrc, Hsrc, filter, Vlines; + int is_50Hz = !(state->std & V4L2_STD_525_60); + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pix = &(fmt->fmt.pix); + + Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4; + Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4; + + Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; + Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; + + Vlines = pix->height + (is_50Hz ? 4 : 7); + + if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || + (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { + CX18_ERR("%dx%d is not a valid size!\n", + pix->width, pix->height); + return -ERANGE; + } + + HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20); + VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); + VSC &= 0x1fff; + + if (pix->width >= 385) + filter = 0; + else if (pix->width > 192) + filter = 1; + else if (pix->width > 96) + filter = 2; + else + filter = 3; + + CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n", + pix->width, pix->height, HSC, VSC); + + /* HSCALE=HSC */ + cx18_av_write(cx, 0x418, HSC & 0xff); + cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff); + cx18_av_write(cx, 0x41a, HSC >> 16); + /* VSCALE=VSC */ + cx18_av_write(cx, 0x41c, VSC & 0xff); + cx18_av_write(cx, 0x41d, VSC >> 8); + /* VS_INTRLACE=1 VFILT=filter */ + cx18_av_write(cx, 0x41e, 0x8 | filter); + break; + + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); + + case V4L2_BUF_TYPE_VBI_CAPTURE: + return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_tuner *vt = arg; + struct v4l2_routing *route = arg; + + /* ignore these commands */ + switch (cmd) { + case TUNER_SET_TYPE_ADDR: + return 0; + } + + if (!state->is_initialized) { + CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd); + /* initialize on first use */ + state->is_initialized = 1; + cx18_av_initialize(cx); + } + + switch (cmd) { + case VIDIOC_INT_DECODE_VBI_LINE: + return cx18_av_vbi(cx, cmd, arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return cx18_av_audio(cx, cmd, arg); + + case VIDIOC_STREAMON: + CX18_DEBUG_INFO("enable output\n"); + cx18_av_write(cx, 0x115, 0x8c); + cx18_av_write(cx, 0x116, 0x07); + break; + + case VIDIOC_STREAMOFF: + CX18_DEBUG_INFO("disable output\n"); + cx18_av_write(cx, 0x115, 0x00); + cx18_av_write(cx, 0x116, 0x00); + break; + + case VIDIOC_LOG_STATUS: + log_video_status(cx); + log_audio_status(cx); + break; + + case VIDIOC_G_CTRL: + return get_v4lctrl(cx, (struct v4l2_control *)arg); + + case VIDIOC_S_CTRL: + return set_v4lctrl(cx, (struct v4l2_control *)arg); + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + break; + } + + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } + return -EINVAL; + } + + case VIDIOC_G_STD: + *(v4l2_std_id *)arg = state->std; + break; + + case VIDIOC_S_STD: + if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) + return 0; + state->radio = 0; + state->std = *(v4l2_std_id *)arg; + return set_v4lstd(cx); + + case AUDC_SET_RADIO: + state->radio = 1; + break; + + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = state->vid_input; + route->output = 0; + break; + + case VIDIOC_INT_S_VIDEO_ROUTING: + return set_input(cx, route->input, state->aud_input); + + case VIDIOC_INT_G_AUDIO_ROUTING: + route->input = state->aud_input; + route->output = 0; + break; + + case VIDIOC_INT_S_AUDIO_ROUTING: + return set_input(cx, state->vid_input, route->input); + + case VIDIOC_S_FREQUENCY: + input_change(cx); + break; + + case VIDIOC_G_TUNER: + { + u8 vpres = cx18_av_read(cx, 0x40e) & 0x20; + u8 mode; + int val = 0; + + if (state->radio) + break; + + vt->signal = vpres ? 0xffff : 0x0; + + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + + mode = cx18_av_read(cx, 0x804); + + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; + + vt->rxsubchans = val; + vt->audmode = state->audmode; + break; + } + + case VIDIOC_S_TUNER: + if (state->radio) + break; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + /* mono -> mono + stereo -> mono + bilingual -> lang1 */ + cx18_av_and_or(cx, 0x809, ~0xf, 0x00); + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + /* mono -> mono + stereo -> stereo + bilingual -> lang1 */ + cx18_av_and_or(cx, 0x809, ~0xf, 0x04); + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang1/lang2 */ + cx18_av_and_or(cx, 0x809, ~0xf, 0x07); + break; + case V4L2_TUNER_MODE_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang2 */ + cx18_av_and_or(cx, 0x809, ~0xf, 0x01); + break; + default: + return -EINVAL; + } + state->audmode = vt->audmode; + break; + + case VIDIOC_G_FMT: + return get_v4lfmt(cx, (struct v4l2_format *)arg); + + case VIDIOC_S_FMT: + return set_v4lfmt(cx, (struct v4l2_format *)arg); + + case VIDIOC_INT_RESET: + cx18_av_initialize(cx); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* ----------------------------------------------------------------------- */ + +static void log_video_status(struct cx18 *cx) +{ + static const char *const fmt_strs[] = { + "0x0", + "NTSC-M", "NTSC-J", "NTSC-4.43", + "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", + "0x9", "0xA", "0xB", + "SECAM", + "0xD", "0xE", "0xF" + }; + + struct cx18_av_state *state = &cx->av_state; + u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf; + u8 gen_stat1 = cx18_av_read(cx, 0x40d); + u8 gen_stat2 = cx18_av_read(cx, 0x40e); + int vid_input = state->vid_input; + + CX18_INFO("Video signal: %spresent\n", + (gen_stat2 & 0x20) ? "" : "not "); + CX18_INFO("Detected format: %s\n", + fmt_strs[gen_stat1 & 0xf]); + + CX18_INFO("Specified standard: %s\n", + vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + + if (vid_input >= CX18_AV_COMPOSITE1 && + vid_input <= CX18_AV_COMPOSITE8) { + CX18_INFO("Specified video input: Composite %d\n", + vid_input - CX18_AV_COMPOSITE1 + 1); + } else { + CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + } + + CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq); +} + +/* ----------------------------------------------------------------------- */ + +static void log_audio_status(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + u8 download_ctl = cx18_av_read(cx, 0x803); + u8 mod_det_stat0 = cx18_av_read(cx, 0x804); + u8 mod_det_stat1 = cx18_av_read(cx, 0x805); + u8 audio_config = cx18_av_read(cx, 0x808); + u8 pref_mode = cx18_av_read(cx, 0x809); + u8 afc0 = cx18_av_read(cx, 0x80b); + u8 mute_ctl = cx18_av_read(cx, 0x8d3); + int aud_input = state->aud_input; + char *p; + + switch (mod_det_stat0) { + case 0x00: p = "mono"; break; + case 0x01: p = "stereo"; break; + case 0x02: p = "dual"; break; + case 0x04: p = "tri"; break; + case 0x10: p = "mono with SAP"; break; + case 0x11: p = "stereo with SAP"; break; + case 0x12: p = "dual with SAP"; break; + case 0x14: p = "tri with SAP"; break; + case 0xfe: p = "forced mode"; break; + default: p = "not defined"; break; + } + CX18_INFO("Detected audio mode: %s\n", p); + + switch (mod_det_stat1) { + case 0x00: p = "not defined"; break; + case 0x01: p = "EIAJ"; break; + case 0x02: p = "A2-M"; break; + case 0x03: p = "A2-BG"; break; + case 0x04: p = "A2-DK1"; break; + case 0x05: p = "A2-DK2"; break; + case 0x06: p = "A2-DK3"; break; + case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x08: p = "AM-L"; break; + case 0x09: p = "NICAM-BG"; break; + case 0x0a: p = "NICAM-DK"; break; + case 0x0b: p = "NICAM-I"; break; + case 0x0c: p = "NICAM-L"; break; + case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; + case 0x0e: p = "IF FM Radio"; break; + case 0x0f: p = "BTSC"; break; + case 0x10: p = "detected chrominance"; break; + case 0xfd: p = "unknown audio standard"; break; + case 0xfe: p = "forced audio standard"; break; + case 0xff: p = "no detected audio standard"; break; + default: p = "not defined"; break; + } + CX18_INFO("Detected audio standard: %s\n", p); + CX18_INFO("Audio muted: %s\n", + (mute_ctl & 0x2) ? "yes" : "no"); + CX18_INFO("Audio microcontroller: %s\n", + (download_ctl & 0x10) ? "running" : "stopped"); + + switch (audio_config >> 4) { + case 0x00: p = "undefined"; break; + case 0x01: p = "BTSC"; break; + case 0x02: p = "EIAJ"; break; + case 0x03: p = "A2-M"; break; + case 0x04: p = "A2-BG"; break; + case 0x05: p = "A2-DK1"; break; + case 0x06: p = "A2-DK2"; break; + case 0x07: p = "A2-DK3"; break; + case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x09: p = "AM-L"; break; + case 0x0a: p = "NICAM-BG"; break; + case 0x0b: p = "NICAM-DK"; break; + case 0x0c: p = "NICAM-I"; break; + case 0x0d: p = "NICAM-L"; break; + case 0x0e: p = "FM radio"; break; + case 0x0f: p = "automatic detection"; break; + default: p = "undefined"; break; + } + CX18_INFO("Configured audio standard: %s\n", p); + + if ((audio_config >> 4) < 0xF) { + switch (audio_config & 0xF) { + case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; + case 0x01: p = "MONO2 (LANGUAGE B)"; break; + case 0x02: p = "MONO3 (STEREO forced MONO)"; break; + case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; + case 0x04: p = "STEREO"; break; + case 0x05: p = "DUAL1 (AC)"; break; + case 0x06: p = "DUAL2 (BC)"; break; + case 0x07: p = "DUAL3 (AB)"; break; + default: p = "undefined"; + } + CX18_INFO("Configured audio mode: %s\n", p); + } else { + switch (audio_config & 0xF) { + case 0x00: p = "BG"; break; + case 0x01: p = "DK1"; break; + case 0x02: p = "DK2"; break; + case 0x03: p = "DK3"; break; + case 0x04: p = "I"; break; + case 0x05: p = "L"; break; + case 0x06: p = "BTSC"; break; + case 0x07: p = "EIAJ"; break; + case 0x08: p = "A2-M"; break; + case 0x09: p = "FM Radio (4.5 MHz)"; break; + case 0x0a: p = "FM Radio (5.5 MHz)"; break; + case 0x0b: p = "S-Video"; break; + case 0x0f: p = "automatic standard and mode detection"; break; + default: p = "undefined"; break; + } + CX18_INFO("Configured audio system: %s\n", p); + } + + if (aud_input) + CX18_INFO("Specified audio input: Tuner (In%d)\n", + aud_input); + else + CX18_INFO("Specified audio input: External\n"); + + switch (pref_mode & 0xf) { + case 0: p = "mono/language A"; break; + case 1: p = "language B"; break; + case 2: p = "language C"; break; + case 3: p = "analog fallback"; break; + case 4: p = "stereo"; break; + case 5: p = "language AC"; break; + case 6: p = "language BC"; break; + case 7: p = "language AB"; break; + default: p = "undefined"; break; + } + CX18_INFO("Preferred audio mode: %s\n", p); + + if ((audio_config & 0xf) == 0xf) { + switch ((afc0 >> 3) & 0x1) { + case 0: p = "system DK"; break; + case 1: p = "system L"; break; + } + CX18_INFO("Selected 65 MHz format: %s\n", p); + + switch (afc0 & 0x7) { + case 0: p = "Chroma"; break; + case 1: p = "BTSC"; break; + case 2: p = "EIAJ"; break; + case 3: p = "A2-M"; break; + case 4: p = "autodetect"; break; + default: p = "undefined"; break; + } + CX18_INFO("Selected 45 MHz format: %s\n", p); + } +} diff --git a/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h new file mode 100644 index 000000000..786901d72 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-av-core.h @@ -0,0 +1,318 @@ +/* + * cx18 ADEC header + * + * Derived from cx25840-core.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX18_AV_CORE_H_ +#define _CX18_AV_CORE_H_ + +struct cx18; + +enum cx18_av_video_input { + /* Composite video inputs In1-In8 */ + CX18_AV_COMPOSITE1 = 1, + CX18_AV_COMPOSITE2, + CX18_AV_COMPOSITE3, + CX18_AV_COMPOSITE4, + CX18_AV_COMPOSITE5, + CX18_AV_COMPOSITE6, + CX18_AV_COMPOSITE7, + CX18_AV_COMPOSITE8, + + /* S-Video inputs consist of one luma input (In1-In4) ORed with one + chroma input (In5-In8) */ + CX18_AV_SVIDEO_LUMA1 = 0x10, + CX18_AV_SVIDEO_LUMA2 = 0x20, + CX18_AV_SVIDEO_LUMA3 = 0x30, + CX18_AV_SVIDEO_LUMA4 = 0x40, + CX18_AV_SVIDEO_CHROMA4 = 0x400, + CX18_AV_SVIDEO_CHROMA5 = 0x500, + CX18_AV_SVIDEO_CHROMA6 = 0x600, + CX18_AV_SVIDEO_CHROMA7 = 0x700, + CX18_AV_SVIDEO_CHROMA8 = 0x800, + + /* S-Video aliases for common luma/chroma combinations */ + CX18_AV_SVIDEO1 = 0x510, + CX18_AV_SVIDEO2 = 0x620, + CX18_AV_SVIDEO3 = 0x730, + CX18_AV_SVIDEO4 = 0x840, +}; + +enum cx18_av_audio_input { + /* Audio inputs: serial or In4-In8 */ + CX18_AV_AUDIO_SERIAL, + CX18_AV_AUDIO4 = 4, + CX18_AV_AUDIO5, + CX18_AV_AUDIO6, + CX18_AV_AUDIO7, + CX18_AV_AUDIO8, +}; + +struct cx18_av_state { + int radio; + v4l2_std_id std; + enum cx18_av_video_input vid_input; + enum cx18_av_audio_input aud_input; + u32 audclk_freq; + int audmode; + int vbi_line_offset; + u32 id; + u32 rev; + int is_initialized; +}; + + +/* Registers */ +#define CXADEC_CHIP_TYPE_TIGER 0x837 +#define CXADEC_CHIP_TYPE_MAKO 0x843 + +#define CXADEC_HOST_REG1 0x000 +#define CXADEC_HOST_REG2 0x001 + +#define CXADEC_CHIP_CTRL 0x100 +#define CXADEC_AFE_CTRL 0x104 +#define CXADEC_PLL_CTRL1 0x108 +#define CXADEC_VID_PLL_FRAC 0x10C +#define CXADEC_AUX_PLL_FRAC 0x110 +#define CXADEC_PIN_CTRL1 0x114 +#define CXADEC_PIN_CTRL2 0x118 +#define CXADEC_PIN_CFG1 0x11C +#define CXADEC_PIN_CFG2 0x120 + +#define CXADEC_PIN_CFG3 0x124 +#define CXADEC_I2S_MCLK 0x127 + +#define CXADEC_AUD_LOCK1 0x128 +#define CXADEC_AUD_LOCK2 0x12C +#define CXADEC_POWER_CTRL 0x130 +#define CXADEC_AFE_DIAG_CTRL1 0x134 +#define CXADEC_AFE_DIAG_CTRL2 0x138 +#define CXADEC_AFE_DIAG_CTRL3 0x13C +#define CXADEC_PLL_DIAG_CTRL 0x140 +#define CXADEC_TEST_CTRL1 0x144 +#define CXADEC_TEST_CTRL2 0x148 +#define CXADEC_BIST_STAT 0x14C +#define CXADEC_DLL1_DIAG_CTRL 0x158 +#define CXADEC_DLL2_DIAG_CTRL 0x15C + +/* IR registers */ +#define CXADEC_IR_CTRL_REG 0x200 +#define CXADEC_IR_TXCLK_REG 0x204 +#define CXADEC_IR_RXCLK_REG 0x208 +#define CXADEC_IR_CDUTY_REG 0x20C +#define CXADEC_IR_STAT_REG 0x210 +#define CXADEC_IR_IRQEN_REG 0x214 +#define CXADEC_IR_FILTER_REG 0x218 +#define CXADEC_IR_FIFO_REG 0x21C + +/* Video Registers */ +#define CXADEC_MODE_CTRL 0x400 +#define CXADEC_OUT_CTRL1 0x404 +#define CXADEC_OUT_CTRL2 0x408 +#define CXADEC_GEN_STAT 0x40C +#define CXADEC_INT_STAT_MASK 0x410 +#define CXADEC_LUMA_CTRL 0x414 + +#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414 +#define CXADEC_CONTRAST_CTRL_BYTE 0x415 +#define CXADEC_LUMA_CTRL_BYTE_3 0x416 + +#define CXADEC_HSCALE_CTRL 0x418 +#define CXADEC_VSCALE_CTRL 0x41C + +#define CXADEC_CHROMA_CTRL 0x420 + +#define CXADEC_USAT_CTRL_BYTE 0x420 +#define CXADEC_VSAT_CTRL_BYTE 0x421 +#define CXADEC_HUE_CTRL_BYTE 0x422 + +#define CXADEC_VBI_LINE_CTRL1 0x424 +#define CXADEC_VBI_LINE_CTRL2 0x428 +#define CXADEC_VBI_LINE_CTRL3 0x42C +#define CXADEC_VBI_LINE_CTRL4 0x430 +#define CXADEC_VBI_LINE_CTRL5 0x434 +#define CXADEC_VBI_FC_CFG 0x438 +#define CXADEC_VBI_MISC_CFG1 0x43C +#define CXADEC_VBI_MISC_CFG2 0x440 +#define CXADEC_VBI_PAY1 0x444 +#define CXADEC_VBI_PAY2 0x448 +#define CXADEC_VBI_CUST1_CFG1 0x44C +#define CXADEC_VBI_CUST1_CFG2 0x450 +#define CXADEC_VBI_CUST1_CFG3 0x454 +#define CXADEC_VBI_CUST2_CFG1 0x458 +#define CXADEC_VBI_CUST2_CFG2 0x45C +#define CXADEC_VBI_CUST2_CFG3 0x460 +#define CXADEC_VBI_CUST3_CFG1 0x464 +#define CXADEC_VBI_CUST3_CFG2 0x468 +#define CXADEC_VBI_CUST3_CFG3 0x46C +#define CXADEC_HORIZ_TIM_CTRL 0x470 +#define CXADEC_VERT_TIM_CTRL 0x474 +#define CXADEC_SRC_COMB_CFG 0x478 +#define CXADEC_CHROMA_VBIOFF_CFG 0x47C +#define CXADEC_FIELD_COUNT 0x480 +#define CXADEC_MISC_TIM_CTRL 0x484 +#define CXADEC_DFE_CTRL1 0x488 +#define CXADEC_DFE_CTRL2 0x48C +#define CXADEC_DFE_CTRL3 0x490 +#define CXADEC_PLL_CTRL2 0x494 +#define CXADEC_HTL_CTRL 0x498 +#define CXADEC_COMB_CTRL 0x49C +#define CXADEC_CRUSH_CTRL 0x4A0 +#define CXADEC_SOFT_RST_CTRL 0x4A4 +#define CXADEC_MV_DT_CTRL2 0x4A8 +#define CXADEC_MV_DT_CTRL3 0x4AC +#define CXADEC_MISC_DIAG_CTRL 0x4B8 + +#define CXADEC_DL_CTL 0x800 +#define CXADEC_DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ +#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ +#define CXADEC_DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ +#define CXADEC_DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ + +#define CXADEC_STD_DET_STATUS 0x804 + +#define CXADEC_STD_DET_CTL 0x808 +#define CXADEC_STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ +#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ + +#define CXADEC_DW8051_INT 0x80C +#define CXADEC_GENERAL_CTL 0x810 +#define CXADEC_AAGC_CTL 0x814 +#define CXADEC_IF_SRC_CTL 0x818 +#define CXADEC_ANLOG_DEMOD_CTL 0x81C +#define CXADEC_ROT_FREQ_CTL 0x820 +#define CXADEC_FM1_CTL 0x824 +#define CXADEC_PDF_CTL 0x828 +#define CXADEC_DFT1_CTL1 0x82C +#define CXADEC_DFT1_CTL2 0x830 +#define CXADEC_DFT_STATUS 0x834 +#define CXADEC_DFT2_CTL1 0x838 +#define CXADEC_DFT2_CTL2 0x83C +#define CXADEC_DFT2_STATUS 0x840 +#define CXADEC_DFT3_CTL1 0x844 +#define CXADEC_DFT3_CTL2 0x848 +#define CXADEC_DFT3_STATUS 0x84C +#define CXADEC_DFT4_CTL1 0x850 +#define CXADEC_DFT4_CTL2 0x854 +#define CXADEC_DFT4_STATUS 0x858 +#define CXADEC_AM_MTS_DET 0x85C +#define CXADEC_ANALOG_MUX_CTL 0x860 +#define CXADEC_DIG_PLL_CTL1 0x864 +#define CXADEC_DIG_PLL_CTL2 0x868 +#define CXADEC_DIG_PLL_CTL3 0x86C +#define CXADEC_DIG_PLL_CTL4 0x870 +#define CXADEC_DIG_PLL_CTL5 0x874 +#define CXADEC_DEEMPH_GAIN_CTL 0x878 +#define CXADEC_DEEMPH_COEF1 0x87C +#define CXADEC_DEEMPH_COEF2 0x880 +#define CXADEC_DBX1_CTL1 0x884 +#define CXADEC_DBX1_CTL2 0x888 +#define CXADEC_DBX1_STATUS 0x88C +#define CXADEC_DBX2_CTL1 0x890 +#define CXADEC_DBX2_CTL2 0x894 +#define CXADEC_DBX2_STATUS 0x898 +#define CXADEC_AM_FM_DIFF 0x89C + +/* NICAM registers go here */ +#define CXADEC_NICAM_STATUS 0x8C8 +#define CXADEC_DEMATRIX_CTL 0x8CC + +#define CXADEC_PATH1_CTL1 0x8D0 +#define CXADEC_PATH1_VOL_CTL 0x8D4 +#define CXADEC_PATH1_EQ_CTL 0x8D8 +#define CXADEC_PATH1_SC_CTL 0x8DC + +#define CXADEC_PATH2_CTL1 0x8E0 +#define CXADEC_PATH2_VOL_CTL 0x8E4 +#define CXADEC_PATH2_EQ_CTL 0x8E8 +#define CXADEC_PATH2_SC_CTL 0x8EC + +#define CXADEC_SRC_CTL 0x8F0 +#define CXADEC_SRC_LF_COEF 0x8F4 +#define CXADEC_SRC1_CTL 0x8F8 +#define CXADEC_SRC2_CTL 0x8FC +#define CXADEC_SRC3_CTL 0x900 +#define CXADEC_SRC4_CTL 0x904 +#define CXADEC_SRC5_CTL 0x908 +#define CXADEC_SRC6_CTL 0x90C + +#define CXADEC_BASEBAND_OUT_SEL 0x910 +#define CXADEC_I2S_IN_CTL 0x914 +#define CXADEC_I2S_OUT_CTL 0x918 +#define CXADEC_AC97_CTL 0x91C +#define CXADEC_QAM_PDF 0x920 +#define CXADEC_QAM_CONST_DEC 0x924 +#define CXADEC_QAM_ROTATOR_FREQ 0x948 + +/* Bit defintions / settings used in Mako Audio */ +#define CXADEC_PREF_MODE_MONO_LANGA 0 +#define CXADEC_PREF_MODE_MONO_LANGB 1 +#define CXADEC_PREF_MODE_MONO_LANGC 2 +#define CXADEC_PREF_MODE_FALLBACK 3 +#define CXADEC_PREF_MODE_STEREO 4 +#define CXADEC_PREF_MODE_DUAL_LANG_AC 5 +#define CXADEC_PREF_MODE_DUAL_LANG_BC 6 +#define CXADEC_PREF_MODE_DUAL_LANG_AB 7 + + +#define CXADEC_DETECT_STEREO 1 +#define CXADEC_DETECT_DUAL 2 +#define CXADEC_DETECT_TRI 4 +#define CXADEC_DETECT_SAP 0x10 +#define CXADEC_DETECT_NO_SIGNAL 0xFF + +#define CXADEC_SELECT_AUDIO_STANDARD_BG 0xF0 /* NICAM BG and A2 BG */ +#define CXADEC_SELECT_AUDIO_STANDARD_DK1 0xF1 /* NICAM DK and A2 DK */ +#define CXADEC_SELECT_AUDIO_STANDARD_DK2 0xF2 +#define CXADEC_SELECT_AUDIO_STANDARD_DK3 0xF3 +#define CXADEC_SELECT_AUDIO_STANDARD_I 0xF4 /* NICAM I and A1 */ +#define CXADEC_SELECT_AUDIO_STANDARD_L 0xF5 /* NICAM L and System L AM */ +#define CXADEC_SELECT_AUDIO_STANDARD_BTSC 0xF6 +#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ 0xF7 +#define CXADEC_SELECT_AUDIO_STANDARD_A2_M 0xF8 /* A2 M */ +#define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ +#define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ + +/* ----------------------------------------------------------------------- */ +/* cx18_av-core.c */ +int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); +int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); +u8 cx18_av_read(struct cx18 *cx, u16 addr); +u32 cx18_av_read4(struct cx18 *cx, u16 addr); +int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); +int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); +int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); + +/* ----------------------------------------------------------------------- */ +/* cx18_av-firmware.c */ +int cx18_av_loadfw(struct cx18 *cx); + +/* ----------------------------------------------------------------------- */ +/* cx18_av-audio.c */ +int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg); +void cx18_av_audio_set_path(struct cx18 *cx); + +/* ----------------------------------------------------------------------- */ +/* cx18_av-vbi.c */ +void cx18_av_vbi_setup(struct cx18 *cx); +int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg); + +#endif diff --git a/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c new file mode 100644 index 000000000..526e14215 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-av-firmware.c @@ -0,0 +1,120 @@ +/* + * cx18 ADEC firmware functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "cx18-driver.h" +#include <linux/firmware.h> + +#define FWFILE "v4l-cx23418-dig.fw" + +int cx18_av_loadfw(struct cx18 *cx) +{ + const struct firmware *fw = NULL; + u32 size; + u32 v; + u8 *ptr; + int i; + + if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { + CX18_ERR("unable to open firmware %s\n", FWFILE); + return -EINVAL; + } + + cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); + cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */ + + /* Reset the Mako core (Register is undocumented.) */ + cx18_av_write4(cx, 0x8100, 0x00010000); + + /* Put the 8051 in reset and enable firmware upload */ + cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); + + ptr = fw->data; + size = fw->size; + + for (i = 0; i < size; i++) { + u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16); + u32 value = 0; + int retries; + + for (retries = 0; retries < 5; retries++) { + cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); + value = cx18_av_read4(cx, CXADEC_DL_CTL); + if ((value & 0x3F00) == (dl_control & 0x3F00)) + break; + } + if (retries >= 5) { + CX18_ERR("unable to load firmware %s\n", FWFILE); + release_firmware(fw); + return -EIO; + } + } + + cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size); + + /* Output to the 416 */ + cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); + + /* Audio input control 1 set to Sony mode */ + /* Audio output input 2 is 0 for slave operation input */ + /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ + /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge + after WS transition for first bit of audio word. */ + cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0); + + /* Audio output control 1 is set to Sony mode */ + /* Audio output control 2 is set to 1 for master mode */ + /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ + /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge + after WS transition for first bit of audio word. */ + /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT + are generated) */ + cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0); + + /* set alt I2s master clock to /16 and enable alt divider i2s + passthrough */ + cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687); + + cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6); + /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */ + + /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ + /* Register 0x09CC is defined by the Merlin firmware, and doesn't + have a name in the spec. */ + cx18_av_write4(cx, 0x09CC, 1); + +#define CX18_AUDIO_ENABLE 0xc72014 + v = read_reg(CX18_AUDIO_ENABLE); + /* If bit 11 is 1 */ + if (v & 0x800) + write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */ + + /* Enable WW auto audio standard detection */ + v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); + v |= 0xFF; /* Auto by default */ + v |= 0x400; /* Stereo by default */ + v |= 0x14000000; + cx18_av_write4(cx, CXADEC_STD_DET_CTL, v); + + release_firmware(fw); + + CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size); + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c new file mode 100644 index 000000000..d09f1daf4 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-av-vbi.c @@ -0,0 +1,413 @@ +/* + * cx18 ADEC VBI functions + * + * Derived from cx25840-vbi.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + +#include "cx18-driver.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int decode_vps(u8 *dst, u8 *p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + + u8 c, err = 0; + int i; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | + ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + + return err & 0xf0; +} + +void cx18_av_vbi_setup(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + v4l2_std_id std = state->std; + int hblank, hactive, burst, vblank, vactive, sc; + int vblank656, src_decimation; + int luma_lpf, uv_lpf, comb; + u32 pll_int, pll_frac, pll_post; + + /* datasheet startup, step 8d */ + if (std & ~V4L2_STD_NTSC) + cx18_av_write(cx, 0x49f, 0x11); + else + cx18_av_write(cx, 0x49f, 0x14); + + if (std & V4L2_STD_625_50) { + hblank = 0x084; + hactive = 0x2d0; + burst = 0x5d; + vblank = 0x024; + vactive = 0x244; + vblank656 = 0x28; + src_decimation = 0x21f; + + luma_lpf = 2; + if (std & V4L2_STD_SECAM) { + uv_lpf = 0; + comb = 0; + sc = 0x0a425f; + } else if (std == V4L2_STD_PAL_Nc) { + uv_lpf = 1; + comb = 0x20; + sc = 556453; + } else { + uv_lpf = 1; + comb = 0x20; + sc = 0x0a8263; + } + } else { + hactive = 720; + hblank = 122; + vactive = 487; + luma_lpf = 1; + uv_lpf = 1; + + src_decimation = 0x21f; + if (std == V4L2_STD_PAL_60) { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + luma_lpf = 2; + comb = 0x20; + sc = 0x0a8263; + } else if (std == V4L2_STD_PAL_M) { + vblank = 20; + vblank656 = 24; + burst = 0x61; + comb = 0x20; + + sc = 555452; + } else { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + comb = 0x66; + sc = 556063; + } + } + + /* DEBUG: Displays configured PLL frequency */ + pll_int = cx18_av_read(cx, 0x108); + pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; + pll_post = cx18_av_read(cx, 0x109); + CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); + + pll >>= 25; + pll /= pll_post; + CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + CX18_DEBUG_INFO("hblank %i, hactive %i, " + "vblank %i , vactive %i, vblank656 %i, src_dec %i," + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," + " sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } + + /* Sets horizontal blanking delay and active lines */ + cx18_av_write(cx, 0x470, hblank); + cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | + (hactive << 4))); + cx18_av_write(cx, 0x472, hactive >> 4); + + /* Sets burst gate delay */ + cx18_av_write(cx, 0x473, burst); + + /* Sets vertical blanking delay and active duration */ + cx18_av_write(cx, 0x474, vblank); + cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | + (vactive << 4))); + cx18_av_write(cx, 0x476, vactive >> 4); + cx18_av_write(cx, 0x477, vblank656); + + /* Sets src decimation rate */ + cx18_av_write(cx, 0x478, 0xff & src_decimation); + cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); + + /* Sets Luma and UV Low pass filters */ + cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); + + /* Enables comb filters */ + cx18_av_write(cx, 0x47b, comb); + + /* Sets SC Step*/ + cx18_av_write(cx, 0x47c, sc); + cx18_av_write(cx, 0x47d, 0xff & sc >> 8); + cx18_av_write(cx, 0x47e, 0xff & sc >> 16); + + /* Sets VBI parameters */ + if (std & V4L2_STD_625_50) { + cx18_av_write(cx, 0x47f, 0x01); + state->vbi_line_offset = 5; + } else { + cx18_av_write(cx, 0x47f, 0x00); + state->vbi_line_offset = 8; + } +} + +int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_format *fmt; + struct v4l2_sliced_vbi_format *svbi; + + switch (cmd) { + case VIDIOC_G_FMT: + { + static u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; + + fmt = arg; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx18_av_read(cx, 0x404) & 0x10) == 0) + break; + + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx18_av_read(cx, 0x424 + i - 7); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx18_av_read(cx, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } + break; + } + + case VIDIOC_S_FMT: + { + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; + + fmt = arg; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (svbi->service_set == 0) { + /* raw VBI */ + memset(svbi, 0, sizeof(*svbi)); + + /* Setup VBI */ + cx18_av_vbi_setup(cx); + + /* VBI Offset */ + cx18_av_write(cx, 0x47f, vbi_offset); + cx18_av_write(cx, 0x404, 0x2e); + break; + } + + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup VBI */ + cx18_av_vbi_setup(cx); + + /* Sliced VBI */ + cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ + cx18_av_write(cx, 0x406, 0x13); + cx18_av_write(cx, 0x47f, vbi_offset); + + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } + + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; + } + } + } + + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx18_av_write(cx, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx18_av_write(cx, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx18_av_write(cx, i, 0); + } + + cx18_av_write(cx, 0x43c, 0x16); + cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22); + break; + } + + case VIDIOC_INT_DECODE_VBI_LINE: + { + struct v4l2_decode_vbi_line *vbi = arg; + u8 *p = vbi->p; + int id1, id2, l, err = 0; + + if (p[0] || p[1] != 0xff || p[2] != 0xff || + (p[3] != 0x55 && p[3] != 0x91)) { + vbi->line = vbi->type = 0; + break; + } + + p += 4; + id1 = p[-1]; + id2 = p[0] & 0xf; + l = p[2] & 0x3f; + l += state->vbi_line_offset; + p += 4; + + switch (id2) { + case 1: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case 4: + id2 = V4L2_SLICED_WSS_625; + break; + case 6: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + id2 = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) + err = 1; + break; + default: + id2 = 0; + err = 1; + break; + } + + vbi->type = err ? 0 : id2; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->p = p; + break; + } + } + + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c new file mode 100644 index 000000000..baccd0792 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -0,0 +1,280 @@ +/* + * cx18 functions to query card hardware + * + * Derived from ivtv-cards.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-cards.h" +#include "cx18-i2c.h" +#include <media/cs5345.h> + +/********************** card configuration *******************************/ + +/* usual i2c tuner addresses to probe */ +static struct cx18_card_tuner_i2c cx18_i2c_std = { + .radio = { I2C_CLIENT_END }, + .demod = { 0x43, I2C_CLIENT_END }, + .tv = { 0x61, 0x60, I2C_CLIENT_END }, +}; + +/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii + This keeps the PCI ID database up to date. Note that the entries + must be added under vendor 0x4444 (Conexant) as subsystem IDs. + New vendor IDs should still be added to the vendor ID list. */ + +/* Hauppauge HVR-1600 cards */ + +/* Note: for Hauppauge cards the tveeprom information is used instead + of PCI IDs */ +static const struct cx18_card cx18_card_hvr1600_esmt = { + .type = CX18_CARD_HVR_1600_ESMT, + .name = "Hauppauge HVR-1600", + .comment = "VBI is not yet supported\n", + .v4l2_capabilities = CX18_CAP_ENCODER, + .hw_audio_ctrl = CX18_HW_CX23418, + .hw_muxer = CX18_HW_CS5345, + .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | + CX18_HW_CS5345 | CX18_HW_DVB, + .video_inputs = { + { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 }, + }, + .audio_inputs = { + { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + { CX18_CARD_INPUT_LINE_IN1, + CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + { CX18_CARD_INPUT_LINE_IN2, + CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO_SERIAL, 0 }, + .ddr = { + /* ESMT M13S128324A-5B memory */ + .chip_config = 0x003, + .refresh = 0x30c, + .timing1 = 0x44220e82, + .timing2 = 0x08, + .tune_lane = 0, + .initial_emrs = 0, + }, + .gpio_init.initial_value = 0x3001, + .gpio_init.direction = 0x3001, + .i2c = &cx18_i2c_std, +}; + +static const struct cx18_card cx18_card_hvr1600_samsung = { + .type = CX18_CARD_HVR_1600_SAMSUNG, + .name = "Hauppauge HVR-1600 (Preproduction)", + .comment = "VBI is not yet supported\n", + .v4l2_capabilities = CX18_CAP_ENCODER, + .hw_audio_ctrl = CX18_HW_CX23418, + .hw_muxer = CX18_HW_CS5345, + .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | + CX18_HW_CS5345 | CX18_HW_DVB, + .video_inputs = { + { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 }, + }, + .audio_inputs = { + { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + { CX18_CARD_INPUT_LINE_IN1, + CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + { CX18_CARD_INPUT_LINE_IN2, + CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO_SERIAL, 0 }, + .ddr = { + /* Samsung K4D263238G-VC33 memory */ + .chip_config = 0x003, + .refresh = 0x30c, + .timing1 = 0x23230b73, + .timing2 = 0x08, + .tune_lane = 0, + .initial_emrs = 2, + }, + .gpio_init.initial_value = 0x3001, + .gpio_init.direction = 0x3001, + .i2c = &cx18_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + +/* Compro VideoMate H900: note that this card is analog only! */ + +static const struct cx18_card_pci_info cx18_pci_h900[] = { + { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 }, + { 0, 0, 0 } +}; + +static const struct cx18_card cx18_card_h900 = { + .type = CX18_CARD_COMPRO_H900, + .name = "Compro VideoMate H900", + .comment = "VBI is not yet supported\n", + .v4l2_capabilities = CX18_CAP_ENCODER, + .hw_audio_ctrl = CX18_HW_CX23418, + .hw_all = CX18_HW_TUNER, + .video_inputs = { + { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX23418_SVIDEO_LUMA3 | CX23418_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE1 }, + }, + .audio_inputs = { + { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO8, 0 }, + { CX18_CARD_INPUT_LINE_IN1, + CX23418_AUDIO_SERIAL, 0 }, + }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO_SERIAL, 0 }, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, + .ddr = { + /* EtronTech EM6A9160TS-5G memory */ + .chip_config = 0x50003, + .refresh = 0x753, + .timing1 = 0x24330e84, + .timing2 = 0x1f, + .tune_lane = 0, + .initial_emrs = 0, + }, + .xceive_pin = 15, + .pci_list = cx18_pci_h900, + .i2c = &cx18_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPC718: not working at the moment! */ + +static const struct cx18_card_pci_info cx18_pci_mpc718[] = { + { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 }, + { 0, 0, 0 } +}; + +static const struct cx18_card cx18_card_mpc718 = { + .type = CX18_CARD_YUAN_MPC718, + .name = "Yuan MPC718", + .comment = "Not yet supported!\n", + .v4l2_capabilities = 0, + .hw_audio_ctrl = CX18_HW_CX23418, + .hw_all = CX18_HW_TUNER, + .video_inputs = { + { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + }, + .audio_inputs = { + { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO8, 0 }, + { CX18_CARD_INPUT_LINE_IN1, + CX23418_AUDIO_SERIAL, 0 }, + }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, + CX23418_AUDIO_SERIAL, 0 }, + .tuners = { + /* XC3028 tuner */ + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, + .ddr = { + /* Probably Samsung K4D263238G-VC33 memory */ + .chip_config = 0x003, + .refresh = 0x30c, + .timing1 = 0x23230b73, + .timing2 = 0x08, + .tune_lane = 0, + .initial_emrs = 2, + }, + .xceive_pin = 15, + .pci_list = cx18_pci_mpc718, + .i2c = &cx18_i2c_std, +}; + +static const struct cx18_card *cx18_card_list[] = { + &cx18_card_hvr1600_esmt, + &cx18_card_hvr1600_samsung, + &cx18_card_h900, + &cx18_card_mpc718, +}; + +const struct cx18_card *cx18_get_card(u16 index) +{ + if (index >= ARRAY_SIZE(cx18_card_list)) + return NULL; + return cx18_card_list[index]; +} + +int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input) +{ + const struct cx18_card_video_input *card_input = + cx->card->video_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "S-Video 1", + "S-Video 2", + "Composite 1", + "Composite 2", + "Composite 3" + }; + + memset(input, 0, sizeof(*input)); + if (index >= cx->nof_inputs) + return -EINVAL; + input->index = index; + strlcpy(input->name, input_strs[card_input->video_type - 1], + sizeof(input->name)); + input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ? + V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); + input->audioset = (1 << cx->nof_audio_inputs) - 1; + input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? + cx->tuner_std : V4L2_STD_ALL; + return 0; +} + +int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio) +{ + const struct cx18_card_audio_input *aud_input = + cx->card->audio_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "Line In 1", + "Line In 2" + }; + + memset(audio, 0, sizeof(*audio)); + if (index >= cx->nof_audio_inputs) + return -EINVAL; + strlcpy(audio->name, input_strs[aud_input->audio_type - 1], + sizeof(audio->name)); + audio->index = index; + audio->capability = V4L2_AUDCAP_STEREO; + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h new file mode 100644 index 000000000..bccb67f0d --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-cards.h @@ -0,0 +1,171 @@ +/* + * cx18 functions to query card hardware + * + * Derived from ivtv-cards.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* hardware flags */ +#define CX18_HW_TUNER (1 << 0) +#define CX18_HW_TVEEPROM (1 << 1) +#define CX18_HW_CS5345 (1 << 2) +#define CX18_HW_GPIO (1 << 3) +#define CX18_HW_CX23418 (1 << 4) +#define CX18_HW_DVB (1 << 5) + +/* video inputs */ +#define CX18_CARD_INPUT_VID_TUNER 1 +#define CX18_CARD_INPUT_SVIDEO1 2 +#define CX18_CARD_INPUT_SVIDEO2 3 +#define CX18_CARD_INPUT_COMPOSITE1 4 +#define CX18_CARD_INPUT_COMPOSITE2 5 +#define CX18_CARD_INPUT_COMPOSITE3 6 + +enum cx34180_video_input { + /* Composite video inputs In1-In8 */ + CX23418_COMPOSITE1 = 1, + CX23418_COMPOSITE2, + CX23418_COMPOSITE3, + CX23418_COMPOSITE4, + CX23418_COMPOSITE5, + CX23418_COMPOSITE6, + CX23418_COMPOSITE7, + CX23418_COMPOSITE8, + + /* S-Video inputs consist of one luma input (In1-In4) ORed with one + chroma input (In5-In8) */ + CX23418_SVIDEO_LUMA1 = 0x10, + CX23418_SVIDEO_LUMA2 = 0x20, + CX23418_SVIDEO_LUMA3 = 0x30, + CX23418_SVIDEO_LUMA4 = 0x40, + CX23418_SVIDEO_CHROMA4 = 0x400, + CX23418_SVIDEO_CHROMA5 = 0x500, + CX23418_SVIDEO_CHROMA6 = 0x600, + CX23418_SVIDEO_CHROMA7 = 0x700, + CX23418_SVIDEO_CHROMA8 = 0x800, + + /* S-Video aliases for common luma/chroma combinations */ + CX23418_SVIDEO1 = 0x510, + CX23418_SVIDEO2 = 0x620, + CX23418_SVIDEO3 = 0x730, + CX23418_SVIDEO4 = 0x840, +}; + +/* audio inputs */ +#define CX18_CARD_INPUT_AUD_TUNER 1 +#define CX18_CARD_INPUT_LINE_IN1 2 +#define CX18_CARD_INPUT_LINE_IN2 3 + +#define CX18_CARD_MAX_VIDEO_INPUTS 6 +#define CX18_CARD_MAX_AUDIO_INPUTS 3 +#define CX18_CARD_MAX_TUNERS 2 + +enum cx23418_audio_input { + /* Audio inputs: serial or In4-In8 */ + CX23418_AUDIO_SERIAL, + CX23418_AUDIO4 = 4, + CX23418_AUDIO5, + CX23418_AUDIO6, + CX23418_AUDIO7, + CX23418_AUDIO8, +}; + +/* V4L2 capability aliases */ +#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE) +/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ + +struct cx18_card_video_input { + u8 video_type; /* video input type */ + u8 audio_index; /* index in cx18_card_audio_input array */ + u16 video_input; /* hardware video input */ +}; + +struct cx18_card_audio_input { + u8 audio_type; /* audio input type */ + u32 audio_input; /* hardware audio input */ + u16 muxer_input; /* hardware muxer input for boards with a + multiplexer chip */ +}; + +struct cx18_card_pci_info { + u16 device; + u16 subsystem_vendor; + u16 subsystem_device; +}; + +/* GPIO definitions */ + +/* The mask is the set of bits used by the operation */ + +struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */ + u32 direction; /* DIR setting. Leave to 0 if no init is needed */ + u32 initial_value; +}; + +struct cx18_card_tuner { + v4l2_std_id std; /* standard for which the tuner is suitable */ + int tuner; /* tuner ID (from tuner.h) */ +}; + +struct cx18_card_tuner_i2c { + unsigned short radio[2];/* radio tuner i2c address to probe */ + unsigned short demod[2];/* demodulator i2c address to probe */ + unsigned short tv[4]; /* tv tuner i2c addresses to probe */ +}; + +struct cx18_ddr { /* DDR config data */ + u32 chip_config; + u32 refresh; + u32 timing1; + u32 timing2; + u32 tune_lane; + u32 initial_emrs; +}; + +/* for card information/parameters */ +struct cx18_card { + int type; + char *name; + char *comment; + u32 v4l2_capabilities; + u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only + 1 dev allowed) */ + u32 hw_muxer; /* hardware used to multiplex audio input */ + u32 hw_all; /* all hardware used by the board */ + struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS]; + struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS]; + struct cx18_card_audio_input radio_input; + + /* GPIO card-specific settings */ + u8 xceive_pin; /* XCeive tuner GPIO reset pin */ + struct cx18_gpio_init gpio_init; + + struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; + struct cx18_card_tuner_i2c *i2c; + + struct cx18_ddr ddr; + + /* list of device and subsystem vendor/devices that + correspond to this card type. */ + const struct cx18_card_pci_info *pci_list; +}; + +int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input); +int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input); +const struct cx18_card *cx18_get_card(u16 index); diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c new file mode 100644 index 000000000..87cf41021 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -0,0 +1,306 @@ +/* + * cx18 ioctl control functions + * + * Derived from ivtv-controls.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-av-core.h" +#include "cx18-cards.h" +#include "cx18-ioctl.h" +#include "cx18-audio.h" +#include "cx18-i2c.h" +#include "cx18-mailbox.h" +#include "cx18-controls.h" + +static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, + V4L2_CID_AUDIO_BALANCE, + V4L2_CID_AUDIO_BASS, + V4L2_CID_AUDIO_TREBLE, + V4L2_CID_AUDIO_MUTE, + V4L2_CID_AUDIO_LOUDNESS, + 0 +}; + +static const u32 *ctrl_classes[] = { + user_ctrls, + cx2341x_mpeg_ctrls, + NULL +}; + +static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl) +{ + const char *name; + + CX18_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id); + + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + switch (qctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + default: + if (cx2341x_ctrl_query(&cx->params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + } + strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); + qctrl->name[sizeof(qctrl->name) - 1] = 0; + return 0; +} + +static int cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + cx18_queryctrl(cx, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); +} + +static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) +{ + s32 v = vctrl->value; + + CX18_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + + default: + CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) +{ + CX18_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + default: + CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt) +{ + if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) + return -EINVAL; + if (atomic_read(&cx->ana_capturing) > 0) + return -EBUSY; + + /* First try to allocate sliced VBI buffers if needed. */ + if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) { + int i; + + for (i = 0; i < CX18_VBI_FRAMES; i++) { + /* Yuck, hardcoded. Needs to be a define */ + cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); + if (cx->vbi.sliced_mpeg_data[i] == NULL) { + while (--i >= 0) { + kfree(cx->vbi.sliced_mpeg_data[i]); + cx->vbi.sliced_mpeg_data[i] = NULL; + } + return -ENOMEM; + } + } + } + + cx->vbi.insert_mpeg = fmt; + + if (cx->vbi.insert_mpeg == 0) + return 0; + /* Need sliced data for mpeg insertion */ + if (cx18_get_service_set(cx->vbi.sliced_in) == 0) { + if (cx->is_60hz) + cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; + else + cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; + cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz); + } + return 0; +} + +int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg) +{ + struct v4l2_control ctrl; + + switch (cmd) { + case VIDIOC_QUERYMENU: + CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n"); + return cx18_querymenu(cx, arg); + + case VIDIOC_QUERYCTRL: + return cx18_queryctrl(cx, arg); + + case VIDIOC_S_CTRL: + return cx18_s_ctrl(cx, arg); + + case VIDIOC_G_CTRL: + return cx18_g_ctrl(cx, arg); + + case VIDIOC_S_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = cx18_s_ctrl(cx, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx2341x_mpeg_params p = cx->params; + int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), arg, cmd); + + if (err) + return err; + + if (p.video_encoding != cx->params.video_encoding) { + int is_mpeg1 = p.video_encoding == + V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_format fmt; + + /* fix videodecoder resolution */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1); + fmt.fmt.pix.height = cx->params.height; + cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt); + } + err = cx2341x_update(cx, cx18_api_func, &cx->params, &p); + if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) + err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); + cx->params = p; + cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; + cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03); + return err; + } + return -EINVAL; + } + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = cx18_g_ctrl(cx, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd); + return -EINVAL; + } + + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&cx->params, + atomic_read(&cx->ana_capturing), arg, cmd); + return -EINVAL; + } + + default: + return -EINVAL; + } + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-controls.h b/linux/drivers/media/video/cx18/cx18-controls.h new file mode 100644 index 000000000..6e985cf42 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-controls.h @@ -0,0 +1,24 @@ +/* + * cx18 ioctl control functions + * + * Derived from ivtv-controls.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg); diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c new file mode 100644 index 000000000..d654b1d6e --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -0,0 +1,964 @@ +/* + * cx18 driver initialization and card probing + * + * Derived from ivtv-driver.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-version.h" +#include "cx18-cards.h" +#include "cx18-i2c.h" +#include "cx18-irq.h" +#include "cx18-gpio.h" +#include "cx18-firmware.h" +#include "cx18-streams.h" +#include "cx18-av-core.h" +#include "cx18-scb.h" +#include "cx18-mailbox.h" +#include "cx18-ioctl.h" +#include "tuner-xc2028.h" + +#include <media/tveeprom.h> + + +/* var to keep track of the number of array elements in use */ +int cx18_cards_active; + +/* If you have already X v4l cards, then set this to X. This way + the device numbers stay matched. Example: you have a WinTV card + without radio and a Compro H900 with. Normally this would give a + video1 device together with a radio0 device for the Compro. By + setting this to 1 you ensure that radio0 is now also radio1. */ +int cx18_first_minor; + +/* Master variable for all cx18 info */ +struct cx18 *cx18_cards[CX18_MAX_CARDS]; + +/* Protects cx18_cards_active */ +DEFINE_SPINLOCK(cx18_cards_lock); + +/* add your revision and whatnot here */ +static struct pci_device_id cx18_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); + +/* Parameter declarations */ +static int cardtype[CX18_MAX_CARDS]; +static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; + +static int cardtype_c = 1; +static int tuner_c = 1; +static int radio_c = 1; +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + +/* Buffers */ +static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; +static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS; +static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS; +static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS; +static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; + +static int cx18_pci_latency = 1; + +int cx18_debug; + +module_param_array(tuner, int, &tuner_c, 0644); +module_param_array(radio, bool, &radio_c, 0644); +module_param_array(cardtype, int, &cardtype_c, 0644); +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); +module_param_named(debug, cx18_debug, int, 0644); +module_param(cx18_pci_latency, int, 0644); +module_param(cx18_first_minor, int, 0644); + +module_param(enc_mpg_buffers, int, 0644); +module_param(enc_ts_buffers, int, 0644); +module_param(enc_yuv_buffers, int, 0644); +module_param(enc_vbi_buffers, int, 0644); +module_param(enc_pcm_buffers, int, 0644); + +MODULE_PARM_DESC(tuner, "Tuner type selection,\n" + "\t\t\tsee tuner.h for values"); +MODULE_PARM_DESC(radio, + "Enable or disable the radio. Use only if autodetection\n" + "\t\t\tfails. 0 = disable, 1 = enable"); +MODULE_PARM_DESC(cardtype, + "Only use this option if your card is not detected properly.\n" + "\t\tSpecify card type:\n" + "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n" + "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n" + "\t\t\t 3 = Compro VideoMate H900\n" + "\t\t\t 4 = Yuan MPC718\n" + "\t\t\t 0 = Autodetect (default)\n" + "\t\t\t-1 = Ignore this card\n\t\t"); +MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); +MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); +MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: 0\n" + "\t\t\t 1/0x0001: warning\n" + "\t\t\t 2/0x0002: info\n" + "\t\t\t 4/0x0004: mailbox\n" + "\t\t\t 8/0x0008: dma\n" + "\t\t\t 16/0x0010: ioctl\n" + "\t\t\t 32/0x0020: file\n" + "\t\t\t 64/0x0040: i2c\n" + "\t\t\t128/0x0080: irq\n" + "\t\t\t256/0x0100: high volume\n"); +MODULE_PARM_DESC(cx18_pci_latency, + "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" + "\t\t\tDefault: Yes"); +MODULE_PARM_DESC(enc_mpg_buffers, + "Encoder MPG Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); +MODULE_PARM_DESC(enc_ts_buffers, + "Encoder TS Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS)); +MODULE_PARM_DESC(enc_yuv_buffers, + "Encoder YUV Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); +MODULE_PARM_DESC(enc_vbi_buffers, + "Encoder VBI Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); +MODULE_PARM_DESC(enc_pcm_buffers, + "Encoder PCM buffers (in MB)\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); + +MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card"); + +MODULE_AUTHOR("Hans Verkuil"); +MODULE_DESCRIPTION("CX23418 driver"); +MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(CX18_VERSION); + +/* Generic utility functions */ +int cx18_msleep_timeout(unsigned int msecs, int intr) +{ + int timeout = msecs_to_jiffies(msecs); + int sig; + + do { + set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); + sig = intr ? signal_pending(current) : 0; + } while (!sig && timeout); + return sig; +} + +/* Release ioremapped memory */ +static void cx18_iounmap(struct cx18 *cx) +{ + if (cx == NULL) + return; + + /* Release io memory */ + if (cx->enc_mem != NULL) { + CX18_DEBUG_INFO("releasing enc_mem\n"); + iounmap(cx->enc_mem); + cx->enc_mem = NULL; + } +} + +/* Hauppauge card? get values from tveeprom */ +void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) +{ + u8 eedata[256]; + + cx->i2c_client[0].addr = 0xA0 >> 1; + tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata)); + tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata); +} + +static void cx18_process_eeprom(struct cx18 *cx) +{ + struct tveeprom tv; + + cx18_read_eeprom(cx, &tv); + + /* Many thanks to Steven Toth from Hauppauge for providing the + model numbers */ + /* Note: the Samsung memory models cannot be reliably determined + from the model number. Use the cardtype module option if you + have one of these preproduction models. */ + switch (tv.model) { + case 74000 ... 74999: + cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); + break; + case 0: + CX18_ERR("Invalid EEPROM\n"); + return; + default: + CX18_ERR("Unknown model %d, defaulting to HVR-1600\n", tv.model); + cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); + break; + } + + cx->v4l2_cap = cx->card->v4l2_capabilities; + cx->card_name = cx->card->name; + cx->card_i2c = cx->card->i2c; + + CX18_INFO("Autodetected %s\n", cx->card_name); + + if (tv.tuner_type == TUNER_ABSENT) + CX18_ERR("tveeprom cannot autodetect tuner!"); + + if (cx->options.tuner == -1) + cx->options.tuner = tv.tuner_type; + if (cx->options.radio == -1) + cx->options.radio = (tv.has_radio != 0); + + if (cx->std != 0) + /* user specified tuner standard */ + return; + + /* autodetect tuner standard */ + if (tv.tuner_formats & V4L2_STD_PAL) { + CX18_DEBUG_INFO("PAL tuner detected\n"); + cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; + } else if (tv.tuner_formats & V4L2_STD_NTSC) { + CX18_DEBUG_INFO("NTSC tuner detected\n"); + cx->std |= V4L2_STD_NTSC_M; + } else if (tv.tuner_formats & V4L2_STD_SECAM) { + CX18_DEBUG_INFO("SECAM tuner detected\n"); + cx->std |= V4L2_STD_SECAM_L; + } else { + CX18_INFO("No tuner detected, default to NTSC-M\n"); + cx->std |= V4L2_STD_NTSC_M; + } +} + +static v4l2_std_id cx18_parse_std(struct cx18 *cx) +{ + switch (pal[0]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[1] == 'c' || pal[1] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + default: + CX18_WARN("pal= argument not recognised\n"); + return 0; + } + + switch (secam[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[1] == 'C' || secam[1] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + default: + CX18_WARN("secam= argument not recognised\n"); + return 0; + } + + switch (ntsc[0]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + default: + CX18_WARN("ntsc= argument not recognised\n"); + return 0; + } + + /* no match found */ + return 0; +} + +static void cx18_process_options(struct cx18 *cx) +{ + int i, j; + + cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; + cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; + cx->options.cardtype = cardtype[cx->num]; + cx->options.tuner = tuner[cx->num]; + cx->options.radio = radio[cx->num]; + + cx->std = cx18_parse_std(cx); + if (cx->options.cardtype == -1) { + CX18_INFO("Ignore card\n"); + return; + } + cx->card = cx18_get_card(cx->options.cardtype - 1); + if (cx->card) + CX18_INFO("User specified %s card\n", cx->card->name); + else if (cx->options.cardtype != 0) + CX18_ERR("Unknown user specified type, trying to autodetect card\n"); + if (cx->card == NULL) { + if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { + cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); + CX18_INFO("Autodetected Hauppauge card\n"); + } + } + if (cx->card == NULL) { + for (i = 0; (cx->card = cx18_get_card(i)); i++) { + if (cx->card->pci_list == NULL) + continue; + for (j = 0; cx->card->pci_list[j].device; j++) { + if (cx->dev->device != + cx->card->pci_list[j].device) + continue; + if (cx->dev->subsystem_vendor != + cx->card->pci_list[j].subsystem_vendor) + continue; + if (cx->dev->subsystem_device != + cx->card->pci_list[j].subsystem_device) + continue; + CX18_INFO("Autodetected %s card\n", cx->card->name); + goto done; + } + } + } +done: + + if (cx->card == NULL) { + cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); + CX18_ERR("Unknown card: vendor/device: %04x/%04x\n", + cx->dev->vendor, cx->dev->device); + CX18_ERR(" subsystem vendor/device: %04x/%04x\n", + cx->dev->subsystem_vendor, cx->dev->subsystem_device); + CX18_ERR("Defaulting to %s card\n", cx->card->name); + CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); + CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); + CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n"); + } + cx->v4l2_cap = cx->card->v4l2_capabilities; + cx->card_name = cx->card->name; + cx->card_i2c = cx->card->i2c; +} + +/* Precondition: the cx18 structure has been memset to 0. Only + the dev and num fields have been filled in. + No assumptions on the card type may be made here (see cx18_init_struct2 + for that). + */ +static int __devinit cx18_init_struct1(struct cx18 *cx) +{ + cx->base_addr = pci_resource_start(cx->dev, 0); + + mutex_init(&cx->serialize_lock); + mutex_init(&cx->i2c_bus_lock[0]); + mutex_init(&cx->i2c_bus_lock[1]); + + spin_lock_init(&cx->lock); + spin_lock_init(&cx->dma_reg_lock); + + /* start counting open_id at 1 */ + cx->open_id = 1; + + /* Initial settings */ + cx2341x_fill_defaults(&cx->params); + cx->temporal_strength = cx->params.video_temporal_filter; + cx->spatial_strength = cx->params.video_spatial_filter; + cx->filter_mode = cx->params.video_spatial_filter_mode | + (cx->params.video_temporal_filter_mode << 1) | + (cx->params.video_median_filter_type << 2); + cx->params.port = CX2341X_PORT_MEMORY; + cx->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI; + init_waitqueue_head(&cx->cap_w); + init_waitqueue_head(&cx->mb_apu_waitq); + init_waitqueue_head(&cx->mb_cpu_waitq); + init_waitqueue_head(&cx->mb_epu_waitq); + init_waitqueue_head(&cx->mb_hpu_waitq); + init_waitqueue_head(&cx->dma_waitq); + + /* VBI */ + cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; + cx->vbi.raw_size = 1456; + cx->vbi.raw_decoder_line_size = 1456; + cx->vbi.raw_decoder_sav_odd_field = 0x20; + cx->vbi.raw_decoder_sav_even_field = 0x60; + cx->vbi.sliced_decoder_line_size = 272; + cx->vbi.sliced_decoder_sav_odd_field = 0xB0; + cx->vbi.sliced_decoder_sav_even_field = 0xF0; + return 0; +} + +/* Second initialization part. Here the card type has been + autodetected. */ +static void __devinit cx18_init_struct2(struct cx18 *cx) +{ + int i; + + for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++) + if (cx->card->video_inputs[i].video_type == 0) + break; + cx->nof_inputs = i; + for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++) + if (cx->card->audio_inputs[i].audio_type == 0) + break; + cx->nof_audio_inputs = i; + + /* Find tuner input */ + for (i = 0; i < cx->nof_inputs; i++) { + if (cx->card->video_inputs[i].video_type == + CX18_CARD_INPUT_VID_TUNER) + break; + } + if (i == cx->nof_inputs) + i = 0; + cx->active_input = i; + cx->audio_input = cx->card->video_inputs[i].audio_index; + cx->av_state.vid_input = CX18_AV_COMPOSITE7; + cx->av_state.aud_input = CX18_AV_AUDIO8; + cx->av_state.audclk_freq = 48000; + cx->av_state.audmode = V4L2_TUNER_MODE_LANG1; + cx->av_state.vbi_line_offset = 8; +} + +static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + u16 cmd; + unsigned char pci_latency; + + CX18_DEBUG_INFO("Enabling pci device\n"); + + if (pci_enable_device(dev)) { + CX18_ERR("Can't enable device %d!\n", cx->num); + return -EIO; + } + if (pci_set_dma_mask(dev, 0xffffffff)) { + CX18_ERR("No suitable DMA available on card %d.\n", cx->num); + return -EIO; + } + if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { + CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num); + return -EIO; + } + + /* Check for bus mastering */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + pci_write_config_word(dev, PCI_COMMAND, cmd); + + pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 64 && cx18_pci_latency) { + CX18_INFO("Unreasonably low latency timer, " + "setting to 64 (was %d)\n", pci_latency); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + } + /* This config space value relates to DMA latencies. The + default value 0x8080 is too low however and will lead + to DMA errors. 0xffff is the max value which solves + these problems. */ + pci_write_config_dword(dev, 0x40, 0xffff); + + CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " + "irq: %d, latency: %d, memory: 0x%lx\n", + cx->dev->device, cx->card_rev, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + cx->dev->irq, pci_latency, (unsigned long)cx->base_addr); + + return 0; +} + +#ifdef MODULE +static u32 cx18_request_module(struct cx18 *cx, u32 hw, + const char *name, u32 id) +{ + if ((hw & id) == 0) + return hw; + if (request_module(name) != 0) { + CX18_ERR("Failed to load module %s\n", name); + return hw & ~id; + } + CX18_DEBUG_INFO("Loaded module %s\n", name); + return hw; +} +#endif + +static void cx18_load_and_init_modules(struct cx18 *cx) +{ + u32 hw = cx->card->hw_all; + int i; + +#ifdef MODULE + /* load modules */ +#ifndef CONFIG_MEDIA_TUNER + hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER); +#endif +#ifndef CONFIG_VIDEO_CS5345 + hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345); +#endif +#endif + + /* check which i2c devices are actually found */ + for (i = 0; i < 32; i++) { + u32 device = 1 << i; + + if (!(device & hw)) + continue; + if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM || + device == CX18_HW_CX23418 || device == CX18_HW_DVB) { + /* These 'devices' do not use i2c probing */ + cx->hw_flags |= device; + continue; + } + cx18_i2c_register(cx, i); + if (cx18_i2c_hw_addr(cx, device) > 0) + cx->hw_flags |= device; + } + + hw = cx->hw_flags; +} + +static int __devinit cx18_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + int retval = 0; + int vbi_buf_size; + u32 devtype; + struct cx18 *cx; + + spin_lock(&cx18_cards_lock); + + /* Make sure we've got a place for this card */ + if (cx18_cards_active == CX18_MAX_CARDS) { + printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n", + cx18_cards_active); + spin_unlock(&cx18_cards_lock); + return -ENOMEM; + } + + cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); + if (!cx) { + spin_unlock(&cx18_cards_lock); + return -ENOMEM; + } + cx18_cards[cx18_cards_active] = cx; + cx->dev = dev; + cx->num = cx18_cards_active++; + snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num); + CX18_INFO("Initializing card #%d\n", cx->num); + + spin_unlock(&cx18_cards_lock); + + cx18_process_options(cx); + if (cx->options.cardtype == -1) { + retval = -ENODEV; + goto err; + } + if (cx18_init_struct1(cx)) { + retval = -ENOMEM; + goto err; + } + + CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); + + /* PCI Device Setup */ + retval = cx18_setup_pci(cx, dev, pci_id); + if (retval != 0) { + if (retval == -EIO) + goto free_workqueue; + else if (retval == -ENXIO) + goto free_mem; + } + /* save cx in the pci struct for later use */ + pci_set_drvdata(dev, cx); + + /* map io memory */ + CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); + cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET, + CX18_MEM_SIZE); + if (!cx->enc_mem) { + CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; + devtype = read_reg(0xC72028); + switch (devtype & 0xff000000) { + case 0xff000000: + CX18_INFO("cx23418 revision %08x (A)\n", devtype); + break; + case 0x01000000: + CX18_INFO("cx23418 revision %08x (B)\n", devtype); + break; + default: + CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype); + break; + } + + cx18_init_power(cx, 1); + cx18_init_memory(cx); + + cx->scb = (struct cx18_scb *)(cx->enc_mem + SCB_OFFSET); + cx18_init_scb(cx); + + cx18_gpio_init(cx); + + /* active i2c */ + CX18_DEBUG_INFO("activating i2c...\n"); + if (init_cx18_i2c(cx)) { + CX18_ERR("Could not initialize i2c\n"); + goto free_map; + } + + CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active); + + if (cx->card->hw_all & CX18_HW_TVEEPROM) { + /* Based on the model number the cardtype may be changed. + The PCI IDs are not always reliable. */ + cx18_process_eeprom(cx); + } + if (cx->card->comment) + CX18_INFO("%s", cx->card->comment); + if (cx->card->v4l2_capabilities == 0) { + retval = -ENODEV; + goto free_i2c; + } + cx18_init_memory(cx); + + /* Register IRQ */ + retval = request_irq(cx->dev->irq, cx18_irq_handler, + IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx); + if (retval) { + CX18_ERR("Failed to register irq %d\n", retval); + goto free_i2c; + } + + if (cx->std == 0) + cx->std = V4L2_STD_NTSC_M; + + if (cx->options.tuner == -1) { + int i; + + for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { + if ((cx->std & cx->card->tuners[i].std) == 0) + continue; + cx->options.tuner = cx->card->tuners[i].tuner; + break; + } + } + /* if no tuner was found, then pick the first tuner in the card list */ + if (cx->options.tuner == -1 && cx->card->tuners[0].std) { + cx->std = cx->card->tuners[0].std; + cx->options.tuner = cx->card->tuners[0].tuner; + } + if (cx->options.radio == -1) + cx->options.radio = (cx->card->radio_input.audio_type != 0); + + /* The card is now fully identified, continue with card-specific + initialization. */ + cx18_init_struct2(cx); + + cx18_load_and_init_modules(cx); + + if (cx->std & V4L2_STD_525_60) { + cx->is_60hz = 1; + cx->is_out_60hz = 1; + } else { + cx->is_50hz = 1; + cx->is_out_50hz = 1; + } + cx->params.video_gop_size = cx->is_60hz ? 15 : 12; + + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000; + vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; + + if (cx->options.radio > 0) + cx->v4l2_cap |= V4L2_CAP_RADIO; + + retval = cx18_streams_setup(cx); + if (retval) { + CX18_ERR("Error %d setting up streams\n", retval); + goto free_irq; + } + retval = cx18_streams_register(cx); + if (retval) { + CX18_ERR("Error %d registering devices\n", retval); + goto free_streams; + } + + if (cx->options.tuner > -1) { + struct tuner_setup setup; + + setup.addr = ADDR_UNSET; + setup.type = cx->options.tuner; + setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ + setup.tuner_callback = (setup.type == TUNER_XC2028) ? + cx18_reset_tuner_gpio : NULL; + cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup); + if (setup.type == TUNER_XC2028) { + static struct xc2028_ctrl ctrl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + }; + struct v4l2_priv_tun_config cfg = { + .tuner = cx->options.tuner, + .priv = &ctrl, + }; + cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg); + } + } + + /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) + are not. */ + cx->tuner_std = cx->std; + + cx18_init_on_first_open(cx); + + CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name); + + return 0; + +free_streams: + cx18_streams_cleanup(cx, 1); +free_irq: + free_irq(cx->dev->irq, (void *)cx); +free_i2c: + exit_cx18_i2c(cx); +free_map: + cx18_iounmap(cx); +free_mem: + release_mem_region(cx->base_addr, CX18_MEM_SIZE); +free_workqueue: +err: + if (retval == 0) + retval = -ENODEV; + CX18_ERR("Error %d on initialization\n", retval); + + kfree(cx18_cards[cx18_cards_active]); + cx18_cards[cx18_cards_active] = NULL; + return retval; +} + +int cx18_init_on_first_open(struct cx18 *cx) +{ + int video_input; + int fw_retry_count = 3; + struct v4l2_frequency vf; + + if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) + return -ENXIO; + + if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags)) + return 0; + + while (--fw_retry_count > 0) { + /* load firmware */ + if (cx18_firmware_init(cx) == 0) + break; + if (fw_retry_count > 1) + CX18_WARN("Retry loading firmware\n"); + } + + if (fw_retry_count == 0) { + set_bit(CX18_F_I_FAILED, &cx->i_flags); + return -ENXIO; + } + set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); + + /* Init the firmware twice to work around a silicon bug + * transport related. */ + + fw_retry_count = 3; + while (--fw_retry_count > 0) { + /* load firmware */ + if (cx18_firmware_init(cx) == 0) + break; + if (fw_retry_count > 1) + CX18_WARN("Retry loading firmware\n"); + } + + if (fw_retry_count == 0) { + set_bit(CX18_F_I_FAILED, &cx->i_flags); + return -ENXIO; + } + + vf.tuner = 0; + vf.type = V4L2_TUNER_ANALOG_TV; + vf.frequency = 6400; /* the tuner 'baseline' frequency */ + + /* Set initial frequency. For PAL/SECAM broadcasts no + 'default' channel exists AFAIK. */ + if (cx->std == V4L2_STD_NTSC_M_JP) + vf.frequency = 1460; /* ch. 1 91250*16/1000 */ + else if (cx->std & V4L2_STD_NTSC_M) + vf.frequency = 1076; /* ch. 4 67250*16/1000 */ + + video_input = cx->active_input; + cx->active_input++; /* Force update of input */ + cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_INPUT, &video_input); + + /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code + in one place. */ + cx->std++; /* Force full standard initialization */ + cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_STD, &cx->tuner_std); + cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_FREQUENCY, &vf); + return 0; +} + +static void cx18_remove(struct pci_dev *pci_dev) +{ + struct cx18 *cx = pci_get_drvdata(pci_dev); + + CX18_DEBUG_INFO("Removing Card #%d\n", cx->num); + + /* Stop all captures */ + CX18_DEBUG_INFO("Stopping all streams\n"); + if (atomic_read(&cx->tot_capturing) > 0) + cx18_stop_all_captures(cx); + + /* Interrupts */ + sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); + sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + + cx18_halt_firmware(cx); + + cx18_streams_cleanup(cx, 1); + + exit_cx18_i2c(cx); + + free_irq(cx->dev->irq, (void *)cx); + + cx18_iounmap(cx); + + release_mem_region(cx->base_addr, CX18_MEM_SIZE); + + pci_disable_device(cx->dev); + + CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num); +} + +/* define a pci_driver for card detection */ +static struct pci_driver cx18_pci_driver = { + .name = "cx18", + .id_table = cx18_pci_tbl, + .probe = cx18_probe, + .remove = cx18_remove, +}; + +static int module_start(void) +{ + printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); + + memset(cx18_cards, 0, sizeof(cx18_cards)); + + /* Validate parameters */ + if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { + printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n", + CX18_MAX_CARDS - 1); + return -1; + } + + if (cx18_debug < 0 || cx18_debug > 511) { + cx18_debug = 0; + printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); + } + + if (pci_register_driver(&cx18_pci_driver)) { + printk(KERN_ERR "cx18: Error detecting PCI card\n"); + return -ENODEV; + } + printk(KERN_INFO "cx18: End initialization\n"); + return 0; +} + +static void module_cleanup(void) +{ + int i; + + pci_unregister_driver(&cx18_pci_driver); + + for (i = 0; i < cx18_cards_active; i++) { + if (cx18_cards[i] == NULL) + continue; + kfree(cx18_cards[i]); + } +} + +module_init(module_start); +module_exit(module_cleanup); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h new file mode 100644 index 000000000..d2b563f98 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -0,0 +1,516 @@ +/* + * cx18 driver internal defines and structures + * + * Derived from ivtv-driver.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef CX18_DRIVER_H +#define CX18_DRIVER_H + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/list.h> +#include <linux/unistd.h> +#include <linux/byteorder/swab.h> +#include <linux/pagemap.h> +#include <linux/workqueue.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) +#include <linux/mutex.h> +#endif + +#include <linux/dvb/video.h> +#include <linux/dvb/audio.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include "cx18-mailbox.h" +#include "cx18-av-core.h" +#include "cx23418.h" + +/* DVB */ +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" + +#ifndef CONFIG_PCI +# error "This driver requires kernel PCI support." +#endif + +#define CX18_MEM_OFFSET 0x00000000 +#define CX18_MEM_SIZE 0x04000000 +#define CX18_REG_OFFSET 0x02000000 + +/* Maximum cx18 driver instances. */ +#define CX18_MAX_CARDS 32 + +/* Supported cards */ +#define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */ +#define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */ +#define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */ +#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ +#define CX18_CARD_LAST 3 + +#define CX18_ENC_STREAM_TYPE_MPG 0 +#define CX18_ENC_STREAM_TYPE_TS 1 +#define CX18_ENC_STREAM_TYPE_YUV 2 +#define CX18_ENC_STREAM_TYPE_VBI 3 +#define CX18_ENC_STREAM_TYPE_PCM 4 +#define CX18_ENC_STREAM_TYPE_IDX 5 +#define CX18_ENC_STREAM_TYPE_RAD 6 +#define CX18_MAX_STREAMS 7 + +/* system vendor and device IDs */ +#define PCI_VENDOR_ID_CX 0x14f1 +#define PCI_DEVICE_ID_CX23418 0x5b7a + +/* subsystem vendor ID */ +#define CX18_PCI_ID_HAUPPAUGE 0x0070 +#define CX18_PCI_ID_COMPRO 0x185b +#define CX18_PCI_ID_YUAN 0x12ab + +/* ======================================================================== */ +/* ========================== START USER SETTABLE DMA VARIABLES =========== */ +/* ======================================================================== */ + +/* DMA Buffers, Default size in MB allocated */ +#define CX18_DEFAULT_ENC_TS_BUFFERS 1 +#define CX18_DEFAULT_ENC_MPG_BUFFERS 2 +#define CX18_DEFAULT_ENC_IDX_BUFFERS 1 +#define CX18_DEFAULT_ENC_YUV_BUFFERS 2 +#define CX18_DEFAULT_ENC_VBI_BUFFERS 1 +#define CX18_DEFAULT_ENC_PCM_BUFFERS 1 + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 + +/* debugging */ + +/* Flag to turn on high volume debugging */ +#define CX18_DBGFLG_WARN (1 << 0) +#define CX18_DBGFLG_INFO (1 << 1) +#define CX18_DBGFLG_API (1 << 2) +#define CX18_DBGFLG_DMA (1 << 3) +#define CX18_DBGFLG_IOCTL (1 << 4) +#define CX18_DBGFLG_FILE (1 << 5) +#define CX18_DBGFLG_I2C (1 << 6) +#define CX18_DBGFLG_IRQ (1 << 7) +/* Flag to turn on high volume debugging */ +#define CX18_DBGFLG_HIGHVOL (1 << 8) + +/* NOTE: extra space before comma in 'cx->num , ## args' is required for + gcc-2.95, otherwise it won't compile. */ +#define CX18_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & cx18_debug) \ + printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \ + } while (0) +#define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args) +#define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args) +#define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args) +#define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args) +#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args) +#define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args) +#define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args) + +#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \ + do { \ + if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ + printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \ + } while (0) +#define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args) +#define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args) +#define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args) +#define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args) +#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args) +#define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args) +#define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args) + +/* Standard kernel messages */ +#define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args) +#define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args) +#define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args) + +/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ +#define MPEG_FRAME_TYPE_IFRAME 1 +#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 +#define MPEG_FRAME_TYPE_ALL 7 + +#define CX18_MAX_PGM_INDEX (400) + +extern int cx18_debug; + + +struct cx18_options { + int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ + int cardtype; /* force card type on load */ + int tuner; /* set tuner on load */ + int radio; /* enable/disable radio */ +}; + +/* per-buffer bit flags */ +#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ + +/* per-stream, s_flags */ +#define CX18_F_S_CLAIMED 3 /* this stream is claimed */ +#define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ +#define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ +#define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ +#define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ + +/* per-cx18, i_flags */ +#define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */ +#define CX18_F_I_EOS 4 /* End of encoder stream reached */ +#define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */ +#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define CX18_F_I_INITED 21 /* set after first open */ +#define CX18_F_I_FAILED 22 /* set if first open failed */ + +/* These are the VBI types as they appear in the embedded VBI private packets. */ +#define CX18_SLICED_TYPE_TELETEXT_B (1) +#define CX18_SLICED_TYPE_CAPTION_525 (4) +#define CX18_SLICED_TYPE_WSS_625 (5) +#define CX18_SLICED_TYPE_VPS (7) + +struct cx18_buffer { + struct list_head list; + dma_addr_t dma_handle; + u32 id; + unsigned long b_flags; + char *buf; + + u32 bytesused; + u32 readpos; +}; + +struct cx18_queue { + struct list_head list; + u32 buffers; + u32 length; + u32 bytesused; +}; + +struct cx18_dvb { + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + struct dmxdev dmxdev; + struct dvb_adapter dvb_adapter; + struct dvb_demux demux; + struct dvb_frontend *fe; + struct dvb_net dvbnet; + int enabled; + int feeding; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex feedlock; +#else + struct semaphore feedlock; +#endif + +}; + +struct cx18; /* forward reference */ +struct cx18_scb; /* forward reference */ + +struct cx18_stream { + /* These first four fields are always set, even if the stream + is not actually created. */ + struct video_device *v4l2dev; /* NULL when stream not created */ + struct cx18 *cx; /* for ease of use */ + const char *name; /* name of the stream */ + int type; /* stream type */ + u32 handle; /* task handle */ + unsigned mdl_offset; + + u32 id; + spinlock_t qlock; /* locks access to the queues */ + unsigned long s_flags; /* status flags, see above */ + int dma; /* can be PCI_DMA_TODEVICE, + PCI_DMA_FROMDEVICE or + PCI_DMA_NONE */ + u64 dma_pts; + wait_queue_head_t waitq; + + /* Buffer Stats */ + u32 buffers; + u32 buf_size; + u32 buffers_stolen; + + /* Buffer Queues */ + struct cx18_queue q_free; /* free buffers */ + struct cx18_queue q_full; /* full buffers */ + struct cx18_queue q_io; /* waiting for I/O */ + + /* DVB / Digital Transport */ + struct cx18_dvb dvb; +}; + +struct cx18_open_id { + u32 open_id; + int type; + enum v4l2_priority prio; + struct cx18 *cx; +}; + +/* forward declaration of struct defined in cx18-cards.h */ +struct cx18_card; + + +#define CX18_VBI_FRAMES 32 + +/* VBI data */ +struct vbi_info { + u32 enc_size; + u32 frame; + u8 cc_data_odd[256]; + u8 cc_data_even[256]; + int cc_pos; + u8 cc_no_update; + u8 vps[5]; + u8 vps_found; + int wss; + u8 wss_found; + u8 wss_no_update; + u32 raw_decoder_line_size; + u8 raw_decoder_sav_odd_field; + u8 raw_decoder_sav_even_field; + u32 sliced_decoder_line_size; + u8 sliced_decoder_sav_odd_field; + u8 sliced_decoder_sav_even_field; + struct v4l2_format in; + /* convenience pointer to sliced struct in vbi_in union */ + struct v4l2_sliced_vbi_format *sliced_in; + u32 service_set_in; + int insert_mpeg; + + /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + One for /dev/vbi0 and one for /dev/vbi8 */ + struct v4l2_sliced_vbi_data sliced_data[36]; + + /* Buffer for VBI data inserted into MPEG stream. + The first byte is a dummy byte that's never used. + The next 16 bytes contain the MPEG header for the VBI data, + the remainder is the actual VBI data. + The max size accepted by the MPEG VBI reinsertion turns out + to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, + where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is + a single line header byte and 2 * 18 is the number of VBI lines per frame. + + However, it seems that the data must be 1K aligned, so we have to + pad the data until the 1 or 2 K boundary. + + This pointer array will allocate 2049 bytes to store each VBI frame. */ + u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; + u32 sliced_mpeg_size[CX18_VBI_FRAMES]; + struct cx18_buffer sliced_mpeg_buf; + u32 inserted_frame; + + u32 start[2], count; + u32 raw_size; + u32 sliced_size; +}; + +/* Per cx23418, per I2C bus private algo callback data */ +struct cx18_i2c_algo_callback_data { + struct cx18 *cx; + int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */ +}; + +/* Struct to hold info about cx18 cards */ +struct cx18 { + int num; /* board number, -1 during init! */ + char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */ + struct pci_dev *dev; /* PCI device */ + const struct cx18_card *card; /* card information */ + const char *card_name; /* full name of the card */ + const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ + u8 is_50hz; + u8 is_60hz; + u8 is_out_50hz; + u8 is_out_60hz; + u8 nof_inputs; /* number of video inputs */ + u8 nof_audio_inputs; /* number of audio inputs */ + u16 buffer_id; /* buffer ID counter */ + u32 v4l2_cap; /* V4L2 capabilities of card */ + u32 hw_flags; /* Hardware description of the board */ + unsigned mdl_offset; + struct cx18_scb *scb; /* pointer to SCB */ + + struct cx18_av_state av_state; + + /* codec settings */ + struct cx2341x_mpeg_params params; + u32 filter_mode; + u32 temporal_strength; + u32 spatial_strength; + + /* dualwatch */ + unsigned long dualwatch_jiffies; + u16 dualwatch_stereo_mode; + + /* Digitizer type */ + int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ +#else + struct semaphore serialize_lock;/* mutex used to serialize open/close/start/stop/ioctl operations */ +#endif + struct cx18_options options; /* User options */ + int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ + struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ + unsigned long i_flags; /* global cx18 flags */ + atomic_t ana_capturing; /* count number of active analog capture streams */ + atomic_t tot_capturing; /* total count number of active capture streams */ + spinlock_t lock; /* lock access to this struct */ + int search_pack_header; + + spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ + + int open_id; /* incremented each time an open occurs, used as + unique ID. Starts at 1, so 0 can be used as + uninitialized value in the stream->id. */ + + u32 base_addr; + struct v4l2_prio_state prio; + + u8 card_rev; + void __iomem *enc_mem, *reg_mem; + + struct vbi_info vbi; + + u32 pgm_info_offset; + u32 pgm_info_num; + u32 pgm_info_write_idx; + u32 pgm_info_read_idx; + struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX]; + + u64 mpg_data_received; + u64 vbi_data_inserted; + + wait_queue_head_t mb_apu_waitq; + wait_queue_head_t mb_cpu_waitq; + wait_queue_head_t mb_epu_waitq; + wait_queue_head_t mb_hpu_waitq; + wait_queue_head_t cap_w; + /* when the current DMA is finished this queue is woken up */ + wait_queue_head_t dma_waitq; + + /* i2c */ + struct i2c_adapter i2c_adap[2]; + struct i2c_algo_bit_data i2c_algo[2]; + struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; + struct i2c_client i2c_client[2]; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex i2c_bus_lock[2]; +#else + struct semaphore i2c_bus_lock[2]; +#endif + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + + /* gpio */ + u32 gpio_dir; + u32 gpio_val; + + /* v4l2 and User settings */ + + /* codec settings */ + u32 audio_input; + u32 active_input; + u32 active_output; + v4l2_std_id std; + v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ +}; + +/* Globals */ +extern struct cx18 *cx18_cards[]; +extern int cx18_cards_active; +extern int cx18_first_minor; +extern spinlock_t cx18_cards_lock; + +/*==============Prototypes==================*/ + +/* Return non-zero if a signal is pending */ +int cx18_msleep_timeout(unsigned int msecs, int intr); + +/* Read Hauppauge eeprom */ +struct tveeprom; /* forward reference */ +void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); + +/* First-open initialization: load firmware, etc. */ +int cx18_init_on_first_open(struct cx18 *cx); + +/* This is a PCI post thing, where if the pci register is not read, then + the write doesn't always take effect right away. By reading back the + register any pending PCI writes will be performed (in order), and so + you can be sure that the writes are guaranteed to be done. + + Rarely needed, only in some timing sensitive cases. + Apparently if this is not done some motherboards seem + to kill the firmware and get into the broken state until computer is + rebooted. */ +#define write_sync(val, reg) \ + do { writel(val, reg); readl(reg); } while (0) + +#define read_reg(reg) readl(cx->reg_mem + (reg)) +#define write_reg(val, reg) writel(val, cx->reg_mem + (reg)) +#define write_reg_sync(val, reg) \ + do { write_reg(val, reg); read_reg(reg); } while (0) + +#define read_enc(addr) readl(cx->enc_mem + (u32)(addr)) +#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr)) +#define write_enc_sync(val, addr) \ + do { write_enc(val, addr); read_enc(addr); } while (0) + +#define sw1_irq_enable(val) do { \ + write_reg(val, SW1_INT_STATUS); \ + write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \ +} while (0) + +#define sw1_irq_disable(val) \ + write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI); + +#define sw2_irq_enable(val) do { \ + write_reg(val, SW2_INT_STATUS); \ + write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \ +} while (0) + +#define sw2_irq_disable(val) \ + write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI); + +#define setup_page(addr) do { \ + u32 val = read_reg(0xD000F8) & ~0x1f00; \ + write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \ +} while (0) + +#endif /* CX18_DRIVER_H */ diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c new file mode 100644 index 000000000..c9744173f --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -0,0 +1,286 @@ +/* + * cx18 functions for DVB support + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx18-version.h" +#include "cx18-dvb.h" +#include "cx18-streams.h" +#include "cx18-cards.h" +#include "s5h1409.h" +#include "mxl5005s.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 + +static struct mxl5005s_config hauppauge_hvr1600_tuner = { + .i2c_address = 0xC6 >> 1, + .if_freq = IF_FREQ_5380000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C_H, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static struct s5h1409_config hauppauge_hvr1600_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_SERIAL_OUTPUT, + .gpio = S5H1409_GPIO_ON, + .qam_if = 44000, + .inversion = S5H1409_INVERSION_OFF, + .status_mode = S5H1409_DEMODLOCKING, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK + +}; + +static int dvb_register(struct cx18_stream *stream); + +/* Kernel DVB framework calls this when the feed needs to start. + * The CX18 framework should enable the transport DMA handling + * and queue processing. + */ +static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx18_stream *stream = (struct cx18_stream *) demux->priv; + struct cx18 *cx = stream->cx; + int ret = -EINVAL; + u32 v; + + CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", + feed->pid, feed->index); + switch (cx->card->type) { + case CX18_CARD_HVR_1600_ESMT: + case CX18_CARD_HVR_1600_SAMSUNG: + v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL); + v |= 0x00400000; /* Serial Mode */ + v |= 0x00002000; /* Data Length - Byte */ + v |= 0x00010000; /* Error - Polarity */ + v |= 0x00020000; /* Error - Passthru */ + v |= 0x000c0000; /* Error - Ignore */ + write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); + break; + + default: + /* Assumption - Parallel transport - Signalling + * undefined or default. + */ + break; + } + + if (!demux->dmx.frontend) + return -EINVAL; + + if (stream) { + mutex_lock(&stream->dvb.feedlock); + if (stream->dvb.feeding++ == 0) { + CX18_DEBUG_INFO("Starting Transport DMA\n"); + ret = cx18_start_v4l2_encode_stream(stream); + } else + ret = 0; + mutex_unlock(&stream->dvb.feedlock); + } + + return ret; +} + +/* Kernel DVB framework calls this when the feed needs to stop. */ +static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx18_stream *stream = (struct cx18_stream *)demux->priv; + struct cx18 *cx = stream->cx; + int ret = -EINVAL; + + CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", + feed->pid, feed->index); + + if (stream) { + mutex_lock(&stream->dvb.feedlock); + if (--stream->dvb.feeding == 0) { + CX18_DEBUG_INFO("Stopping Transport DMA\n"); + ret = cx18_stop_v4l2_encode_stream(stream, 0); + } else + ret = 0; + mutex_unlock(&stream->dvb.feedlock); + } + + return ret; +} + +int cx18_dvb_register(struct cx18_stream *stream) +{ + struct cx18 *cx = stream->cx; + struct cx18_dvb *dvb = &stream->dvb; + struct dvb_adapter *dvb_adapter; + struct dvb_demux *dvbdemux; + struct dmx_demux *dmx; + int ret; + + if (!dvb) + return -EINVAL; + + ret = dvb_register_adapter(&dvb->dvb_adapter, + CX18_DRIVER_NAME, + THIS_MODULE, &cx->dev->dev, adapter_nr); + if (ret < 0) + goto err_out; + + dvb_adapter = &dvb->dvb_adapter; + + dvbdemux = &dvb->demux; + + dvbdemux->priv = (void *)stream; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = cx18_dvb_start_feed; + dvbdemux->stop_feed = cx18_dvb_stop_feed; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); + ret = dvb_dmx_init(dvbdemux); + if (ret < 0) + goto err_dvb_unregister_adapter; + + dmx = &dvbdemux->dmx; + + dvb->hw_frontend.source = DMX_FRONTEND_0; + dvb->mem_frontend.source = DMX_MEMORY_FE; + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = dmx; + + ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); + if (ret < 0) + goto err_dvb_dmx_release; + + ret = dmx->add_frontend(dmx, &dvb->hw_frontend); + if (ret < 0) + goto err_dvb_dmxdev_release; + + ret = dmx->add_frontend(dmx, &dvb->mem_frontend); + if (ret < 0) + goto err_remove_hw_frontend; + + ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); + if (ret < 0) + goto err_remove_mem_frontend; + + ret = dvb_register(stream); + if (ret < 0) + goto err_disconnect_frontend; + + dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); + + CX18_INFO("DVB Frontend registered\n"); + mutex_init(&dvb->feedlock); + dvb->enabled = 1; + return ret; + +err_disconnect_frontend: + dmx->disconnect_frontend(dmx); +err_remove_mem_frontend: + dmx->remove_frontend(dmx, &dvb->mem_frontend); +err_remove_hw_frontend: + dmx->remove_frontend(dmx, &dvb->hw_frontend); +err_dvb_dmxdev_release: + dvb_dmxdev_release(&dvb->dmxdev); +err_dvb_dmx_release: + dvb_dmx_release(dvbdemux); +err_dvb_unregister_adapter: + dvb_unregister_adapter(dvb_adapter); +err_out: + return ret; +} + +void cx18_dvb_unregister(struct cx18_stream *stream) +{ + struct cx18 *cx = stream->cx; + struct cx18_dvb *dvb = &stream->dvb; + struct dvb_adapter *dvb_adapter; + struct dvb_demux *dvbdemux; + struct dmx_demux *dmx; + + CX18_INFO("unregister DVB\n"); + + dvb_adapter = &dvb->dvb_adapter; + dvbdemux = &dvb->demux; + dmx = &dvbdemux->dmx; + + dmx->close(dmx); + dvb_net_release(&dvb->dvbnet); + dmx->remove_frontend(dmx, &dvb->mem_frontend); + dmx->remove_frontend(dmx, &dvb->hw_frontend); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(dvbdemux); + dvb_unregister_frontend(dvb->fe); + dvb_frontend_detach(dvb->fe); + dvb_unregister_adapter(dvb_adapter); +} + +/* All the DVB attach calls go here, this function get's modified + * for each new card. No other function in this file needs + * to change. + */ +static int dvb_register(struct cx18_stream *stream) +{ + struct cx18_dvb *dvb = &stream->dvb; + struct cx18 *cx = stream->cx; + int ret = 0; + + switch (cx->card->type) { + case CX18_CARD_HVR_1600_ESMT: + case CX18_CARD_HVR_1600_SAMSUNG: + dvb->fe = dvb_attach(s5h1409_attach, + &hauppauge_hvr1600_config, + &cx->i2c_adap[0]); + if (dvb->fe != NULL) { + dvb_attach(mxl5005s_attach, dvb->fe, + &cx->i2c_adap[0], + &hauppauge_hvr1600_tuner); + ret = 0; + } + break; + default: + /* No Digital Tv Support */ + break; + } + + if (dvb->fe == NULL) { + CX18_ERR("frontend initialization failed\n"); + return -1; + } + + ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); + if (ret < 0) { + if (dvb->fe->ops.release) + dvb->fe->ops.release(dvb->fe); + return ret; + } + + return ret; +} diff --git a/linux/drivers/media/video/cx18/cx18-dvb.h b/linux/drivers/media/video/cx18/cx18-dvb.h new file mode 100644 index 000000000..d6a6ccda7 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-dvb.h @@ -0,0 +1,25 @@ +/* + * cx18 functions for DVB support + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx18-driver.h" + +int cx18_dvb_register(struct cx18_stream *stream); +void cx18_dvb_unregister(struct cx18_stream *stream); diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c new file mode 100644 index 000000000..f5f82c487 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -0,0 +1,748 @@ +/* + * cx18 file operation functions + * + * Derived from ivtv-fileops.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-fileops.h" +#include "cx18-i2c.h" +#include "cx18-queue.h" +#include "cx18-vbi.h" +#include "cx18-audio.h" +#include "cx18-mailbox.h" +#include "cx18-scb.h" +#include "cx18-streams.h" +#include "cx18-controls.h" +#include "cx18-ioctl.h" +#include "cx18-cards.h" + +/* This function tries to claim the stream for a specific file descriptor. + If no one else is using this stream then the stream is claimed and + associated VBI streams are also automatically claimed. + Possible error returns: -EBUSY if someone else has claimed + the stream or 0 on success. */ +static int cx18_claim_stream(struct cx18_open_id *id, int type) +{ + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[type]; + struct cx18_stream *s_vbi; + int vbi_type; + + if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { + /* someone already claimed this stream */ + if (s->id == id->open_id) { + /* yes, this file descriptor did. So that's OK. */ + return 0; + } + if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) { + /* VBI is handled already internally, now also assign + the file descriptor to this stream for external + reading of the stream. */ + s->id = id->open_id; + CX18_DEBUG_INFO("Start Read VBI\n"); + return 0; + } + /* someone else is using this stream already */ + CX18_DEBUG_INFO("Stream %d is busy\n", type); + return -EBUSY; + } + s->id = id->open_id; + + /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI, + CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI + (provided VBI insertion is on and sliced VBI is selected), for all + other streams we're done */ + if (type == CX18_ENC_STREAM_TYPE_MPG && + cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) { + vbi_type = CX18_ENC_STREAM_TYPE_VBI; + } else { + return 0; + } + s_vbi = &cx->streams[vbi_type]; + + set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags); + + /* mark that it is used internally */ + set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags); + return 0; +} + +/* This function releases a previously claimed stream. It will take into + account associated VBI streams. */ +static void cx18_release_stream(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + struct cx18_stream *s_vbi; + + s->id = -1; + if (s->type == CX18_ENC_STREAM_TYPE_VBI && + test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) { + /* this stream is still in use internally */ + return; + } + if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) { + CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name); + return; + } + + cx18_flush_queues(s); + + /* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI, + for all other streams we're done */ + if (s->type == CX18_ENC_STREAM_TYPE_MPG) + s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + else + return; + + /* clear internal use flag */ + if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) { + /* was already cleared */ + return; + } + if (s_vbi->id != -1) { + /* VBI stream still claimed by a file descriptor */ + return; + } + clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags); + cx18_flush_queues(s_vbi); +} + +static void cx18_dualwatch(struct cx18 *cx) +{ + struct v4l2_tuner vt; + u16 new_bitmap; + u16 new_stereo_mode; + const u16 stereo_mask = 0x0300; + const u16 dual = 0x0200; + + new_stereo_mode = cx->params.audio_properties & stereo_mask; + memset(&vt, 0, sizeof(vt)); + cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt); + if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && + (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) + new_stereo_mode = dual; + + if (new_stereo_mode == cx->dualwatch_stereo_mode) + return; + + new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask); + + CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", + cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); + + if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, + cx18_find_handle(cx), new_bitmap) == 0) { + cx->dualwatch_stereo_mode = new_stereo_mode; + return; + } + CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); +} + +#if 0 +static void cx18_update_pgm_info(struct cx18 *cx) +{ + u32 wr_idx = (read_enc(cx->pgm_info_offset) - cx->pgm_info_offset - 4) / 24; + int cnt; + int i = 0; + + if (wr_idx >= cx->pgm_info_num) { + CX18_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, cx->pgm_info_num); + return; + } + cnt = (wr_idx + cx->pgm_info_num - cx->pgm_info_write_idx) % cx->pgm_info_num; + while (i < cnt) { + int idx = (cx->pgm_info_write_idx + i) % cx->pgm_info_num; + struct v4l2_enc_idx_entry *e = cx->pgm_info + idx; + u32 addr = cx->pgm_info_offset + 4 + idx * 24; + const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 }; + + e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); + if (e->offset > cx->mpg_data_received) + break; + e->offset += cx->vbi_data_inserted; + e->length = read_enc(addr); + e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); + e->flags = mapping[read_enc(addr + 12) & 3]; + i++; + } + cx->pgm_info_write_idx = (cx->pgm_info_write_idx + i) % cx->pgm_info_num; +} +#endif + +static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err) +{ + struct cx18 *cx = s->cx; + struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + struct cx18_buffer *buf; + DEFINE_WAIT(wait); + + *err = 0; + while (1) { + if (s->type == CX18_ENC_STREAM_TYPE_MPG) { +#if 0 + /* Process pending program info updates and pending + VBI data */ + cx18_update_pgm_info(cx); +#endif + + if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { + cx->dualwatch_jiffies = jiffies; + cx18_dualwatch(cx); + } + if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { + while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { + /* byteswap and process VBI data */ +/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */ + cx18_enqueue(s_vbi, buf, &s_vbi->q_free); + } + } + buf = &cx->vbi.sliced_mpeg_buf; + if (buf->readpos != buf->bytesused) + return buf; + } + + /* do we have leftover data? */ + buf = cx18_dequeue(s, &s->q_io); + if (buf) + return buf; + + /* do we have new data? */ + buf = cx18_dequeue(s, &s->q_full); + if (buf) { + if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP, + &buf->b_flags)) + return buf; + if (s->type == CX18_ENC_STREAM_TYPE_MPG) + /* byteswap MPG data */ + cx18_buf_swap(buf); + else { + /* byteswap and process VBI data */ + cx18_process_vbi_data(cx, buf, + s->dma_pts, s->type); + } + return buf; + } + + /* return if end of stream */ + if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) { + CX18_DEBUG_INFO("EOS %s\n", s->name); + return NULL; + } + + /* return if file was opened with O_NONBLOCK */ + if (non_block) { + *err = -EAGAIN; + return NULL; + } + + /* wait for more data to arrive */ + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become available before we were added + to the waitqueue */ + if (!s->q_full.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + CX18_DEBUG_INFO("User stopped %s\n", s->name); + *err = -EINTR; + return NULL; + } + } +} + +static void cx18_setup_sliced_vbi_buf(struct cx18 *cx) +{ + int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; + + cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx]; + cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx]; + cx->vbi.sliced_mpeg_buf.readpos = 0; +} + +static size_t cx18_copy_buf_to_user(struct cx18_stream *s, + struct cx18_buffer *buf, char __user *ubuf, size_t ucount) +{ + struct cx18 *cx = s->cx; + size_t len = buf->bytesused - buf->readpos; + + if (len > ucount) + len = ucount; + if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && + cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) { + const char *start = buf->buf + buf->readpos; + const char *p = start + 1; + const u8 *q; + u8 ch = cx->search_pack_header ? 0xba : 0xe0; + int stuffing, i; + + while (start + len > p) { + q = memchr(p, 0, start + len - p); + if (q == NULL) + break; + p = q + 1; + if ((char *)q + 15 >= buf->buf + buf->bytesused || + q[1] != 0 || q[2] != 1 || q[3] != ch) + continue; + if (!cx->search_pack_header) { + if ((q[6] & 0xc0) != 0x80) + continue; + if (((q[7] & 0xc0) == 0x80 && + (q[9] & 0xf0) == 0x20) || + ((q[7] & 0xc0) == 0xc0 && + (q[9] & 0xf0) == 0x30)) { + ch = 0xba; + cx->search_pack_header = 1; + p = q + 9; + } + continue; + } + stuffing = q[13] & 7; + /* all stuffing bytes must be 0xff */ + for (i = 0; i < stuffing; i++) + if (q[14 + i] != 0xff) + break; + if (i == stuffing && + (q[4] & 0xc4) == 0x44 && + (q[12] & 3) == 3 && + q[14 + stuffing] == 0 && + q[15 + stuffing] == 0 && + q[16 + stuffing] == 1) { + cx->search_pack_header = 0; + len = (char *)q - start; + cx18_setup_sliced_vbi_buf(cx); + break; + } + } + } + if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { + CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n", + len, s->name); + return -EFAULT; + } + buf->readpos += len; + if (s->type == CX18_ENC_STREAM_TYPE_MPG && + buf != &cx->vbi.sliced_mpeg_buf) + cx->mpg_data_received += len; + return len; +} + +static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, + size_t tot_count, int non_block) +{ + struct cx18 *cx = s->cx; + size_t tot_written = 0; + int single_frame = 0; + + if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) { + /* shouldn't happen */ + CX18_DEBUG_WARN("Stream %s not initialized before read\n", + s->name); + return -EIO; + } + + /* Each VBI buffer is one frame, the v4l2 API says that for VBI the + frames should arrive one-by-one, so make sure we never output more + than one VBI frame at a time */ + if (s->type == CX18_ENC_STREAM_TYPE_VBI && + cx->vbi.sliced_in->service_set) + single_frame = 1; + + for (;;) { + struct cx18_buffer *buf; + int rc; + + buf = cx18_get_buffer(s, non_block, &rc); + /* if there is no data available... */ + if (buf == NULL) { + /* if we got data, then return that regardless */ + if (tot_written) + break; + /* EOS condition */ + if (rc == 0) { + clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); + clear_bit(CX18_F_S_APPL_IO, &s->s_flags); + cx18_release_stream(s); + } + /* set errno */ + return rc; + } + + rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, + tot_count - tot_written); + + if (buf != &cx->vbi.sliced_mpeg_buf) { + if (buf->readpos == buf->bytesused) { + cx18_buf_sync_for_device(s, buf); + cx18_enqueue(s, buf, &s->q_free); + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, + s->handle, + (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, + 1, buf->id, s->buf_size); + } else + cx18_enqueue(s, buf, &s->q_io); + } else if (buf->readpos == buf->bytesused) { + int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; + + cx->vbi.sliced_mpeg_size[idx] = 0; + cx->vbi.inserted_frame++; + cx->vbi_data_inserted += buf->bytesused; + } + if (rc < 0) + return rc; + tot_written += rc; + + if (tot_written == tot_count || single_frame) + break; + } + return tot_written; +} + +static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf, + size_t count, loff_t *pos, int non_block) +{ + ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0; + struct cx18 *cx = s->cx; + + CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc); + if (rc > 0) + pos += rc; + return rc; +} + +int cx18_start_capture(struct cx18_open_id *id) +{ + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + struct cx18_stream *s_vbi; + + if (s->type == CX18_ENC_STREAM_TYPE_RAD) { + /* you cannot read from these stream types. */ + return -EPERM; + } + + /* Try to claim this stream. */ + if (cx18_claim_stream(id, s->type)) + return -EBUSY; + + /* If capture is already in progress, then we also have to + do nothing extra. */ + if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || + test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { + set_bit(CX18_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* Start VBI capture if required */ + s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + if (s->type == CX18_ENC_STREAM_TYPE_MPG && + test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { + /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed + automatically when the MPG stream is claimed. + We only need to start the VBI capturing. */ + if (cx18_start_v4l2_encode_stream(s_vbi)) { + CX18_DEBUG_WARN("VBI capture start failed\n"); + + /* Failure, clean up and return an error */ + clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); + clear_bit(CX18_F_S_STREAMING, &s->s_flags); + /* also releases the associated VBI stream */ + cx18_release_stream(s); + return -EIO; + } + CX18_DEBUG_INFO("VBI insertion started\n"); + } + + /* Tell the card to start capturing */ + if (!cx18_start_v4l2_encode_stream(s)) { + /* We're done */ + set_bit(CX18_F_S_APPL_IO, &s->s_flags); + /* Resume a possibly paused encoder */ + if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) + cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle); + return 0; + } + + /* failure, clean up */ + CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); + + /* Note: the CX18_ENC_STREAM_TYPE_VBI is released + automatically when the MPG stream is released. + We only need to stop the VBI capturing. */ + if (s->type == CX18_ENC_STREAM_TYPE_MPG && + test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { + cx18_stop_v4l2_encode_stream(s_vbi, 0); + clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); + } + clear_bit(CX18_F_S_STREAMING, &s->s_flags); + cx18_release_stream(s); + return -EIO; +} + +ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct cx18_open_id *id = filp->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + int rc; + + CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name); + + mutex_lock(&cx->serialize_lock); + rc = cx18_start_capture(id); + mutex_unlock(&cx->serialize_lock); + if (rc) + return rc; + return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); +} + +unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) +{ + struct cx18_open_id *id = filp->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); + + /* Start a capture if there is none */ + if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { + int rc; + + mutex_lock(&cx->serialize_lock); + rc = cx18_start_capture(id); + mutex_unlock(&cx->serialize_lock); + if (rc) { + CX18_DEBUG_INFO("Could not start capture for %s (%d)\n", + s->name, rc); + return POLLERR; + } + CX18_DEBUG_FILE("Encoder poll started capture\n"); + } + + /* add stream's waitq to the poll list */ + CX18_DEBUG_HI_FILE("Encoder poll\n"); + poll_wait(filp, &s->waitq, wait); + + if (s->q_full.length || s->q_io.length) + return POLLIN | POLLRDNORM; + if (eof) + return POLLHUP; + return 0; +} + +void cx18_stop_capture(struct cx18_open_id *id, int gop_end) +{ + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + CX18_DEBUG_IOCTL("close() of %s\n", s->name); + + /* 'Unclaim' this stream */ + + /* Stop capturing */ + if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) { + struct cx18_stream *s_vbi = + &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + + CX18_DEBUG_INFO("close stopping capture\n"); + /* Special case: a running VBI capture for VBI insertion + in the mpeg stream. Need to stop that too. */ + if (id->type == CX18_ENC_STREAM_TYPE_MPG && + test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && + !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { + CX18_DEBUG_INFO("close stopping embedded VBI capture\n"); + cx18_stop_v4l2_encode_stream(s_vbi, 0); + } + if (id->type == CX18_ENC_STREAM_TYPE_VBI && + test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) + /* Also used internally, don't stop capturing */ + s->id = -1; + else + cx18_stop_v4l2_encode_stream(s, gop_end); + } + if (!gop_end) { + clear_bit(CX18_F_S_APPL_IO, &s->s_flags); + clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); + cx18_release_stream(s); + } +} + +int cx18_v4l2_close(struct inode *inode, struct file *filp) +{ + struct cx18_open_id *id = filp->private_data; + struct cx18 *cx = id->cx; + struct cx18_stream *s = &cx->streams[id->type]; + + CX18_DEBUG_IOCTL("close() of %s\n", s->name); + + v4l2_prio_close(&cx->prio, &id->prio); + + /* Easy case first: this stream was never claimed by us */ + if (s->id != id->open_id) { + kfree(id); + return 0; + } + + /* 'Unclaim' this stream */ + + /* Stop radio */ + mutex_lock(&cx->serialize_lock); + if (id->type == CX18_ENC_STREAM_TYPE_RAD) { + /* Closing radio device, return to TV mode */ + cx18_mute(cx); + /* 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_i2c_clients(cx, VIDIOC_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) { + /* Undo video mute */ + cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, + cx->params.video_mute | + (cx->params.video_mute_yuv << 8)); + } + /* Done! Unmute and continue. */ + cx18_unmute(cx); + cx18_release_stream(s); + } else { + cx18_stop_capture(id, 0); + } + kfree(id); + mutex_unlock(&cx->serialize_lock); + return 0; +} + +static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) +{ + struct cx18 *cx = s->cx; + struct cx18_open_id *item; + + CX18_DEBUG_FILE("open %s\n", s->name); + + /* Allocate memory */ + item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); + if (NULL == item) { + CX18_DEBUG_WARN("nomem on v4l2 open\n"); + return -ENOMEM; + } + item->cx = cx; + item->type = s->type; + v4l2_prio_open(&cx->prio, &item->prio); + + item->open_id = cx->open_id++; + filp->private_data = item; + + if (item->type == CX18_ENC_STREAM_TYPE_RAD) { + /* Try to claim this stream */ + if (cx18_claim_stream(item, item->type)) { + /* No, it's already in use */ + kfree(item); + return -EBUSY; + } + + if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { + if (atomic_read(&cx->ana_capturing) > 0) { + /* switching to radio while capture is + in progress is not polite */ + cx18_release_stream(s); + kfree(item); + return -EBUSY; + } + } + + /* Mark that the radio is being used. */ + set_bit(CX18_F_I_RADIO_USER, &cx->i_flags); + /* We have the radio */ + cx18_mute(cx); + /* Switch tuner to radio */ + cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL); + /* Select the correct audio input (i.e. radio tuner) */ + cx18_audio_set_io(cx); + /* Done! Unmute and continue. */ + cx18_unmute(cx); + } + return 0; +} + +int cx18_v4l2_open(struct inode *inode, struct file *filp) +{ + int res, x, y = 0; + struct cx18 *cx = NULL; + struct cx18_stream *s = NULL; + int minor = iminor(inode); + + /* Find which card this open was on */ + spin_lock(&cx18_cards_lock); + for (x = 0; cx == NULL && x < cx18_cards_active; x++) { + /* find out which stream this open was on */ + for (y = 0; y < CX18_MAX_STREAMS; y++) { + if (cx18_cards[x] == NULL) + continue; + s = &cx18_cards[x]->streams[y]; + if (s->v4l2dev && s->v4l2dev->minor == minor) { + cx = cx18_cards[x]; + break; + } + } + } + spin_unlock(&cx18_cards_lock); + + if (cx == NULL) { + /* Couldn't find a device registered + on that minor, shouldn't happen! */ + printk(KERN_WARNING "No cx18 device found on minor %d\n", + minor); + return -ENXIO; + } + + mutex_lock(&cx->serialize_lock); + if (cx18_init_on_first_open(cx)) { + CX18_ERR("Failed to initialize on minor %d\n", minor); + mutex_unlock(&cx->serialize_lock); + return -ENXIO; + } + res = cx18_serialized_open(s, filp); + mutex_unlock(&cx->serialize_lock); + return res; +} + +void cx18_mute(struct cx18 *cx) +{ + if (atomic_read(&cx->ana_capturing)) + cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, + cx18_find_handle(cx), 1); + CX18_DEBUG_INFO("Mute\n"); +} + +void cx18_unmute(struct cx18 *cx) +{ + if (atomic_read(&cx->ana_capturing)) { + cx18_msleep_timeout(100, 0); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, + cx18_find_handle(cx), 12); + cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, + cx18_find_handle(cx), 0); + } + CX18_DEBUG_INFO("Unmute\n"); +} diff --git a/linux/drivers/media/video/cx18/cx18-fileops.h b/linux/drivers/media/video/cx18/cx18-fileops.h new file mode 100644 index 000000000..46da0282f --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-fileops.h @@ -0,0 +1,36 @@ +/* + * cx18 file operation functions + * + * Derived from ivtv-fileops.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +/* Testing/Debugging */ +int cx18_v4l2_open(struct inode *inode, struct file *filp); +ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos); +ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count, + loff_t *pos); +int cx18_v4l2_close(struct inode *inode, struct file *filp); +unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait); +int cx18_start_capture(struct cx18_open_id *id); +void cx18_stop_capture(struct cx18_open_id *id, int gop_end); +void cx18_mute(struct cx18 *cx); +void cx18_unmute(struct cx18 *cx); + diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c new file mode 100644 index 000000000..5e37427ec --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-firmware.c @@ -0,0 +1,380 @@ +/* + * cx18 firmware functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-scb.h" +#include "cx18-irq.h" +#include "cx18-firmware.h" +#include "cx18-cards.h" +#include <linux/firmware.h> + +#define CX18_PROC_SOFT_RESET 0xc70010 +#define CX18_DDR_SOFT_RESET 0xc70014 +#define CX18_CLOCK_SELECT1 0xc71000 +#define CX18_CLOCK_SELECT2 0xc71004 +#define CX18_HALF_CLOCK_SELECT1 0xc71008 +#define CX18_HALF_CLOCK_SELECT2 0xc7100C +#define CX18_CLOCK_POLARITY1 0xc71010 +#define CX18_CLOCK_POLARITY2 0xc71014 +#define CX18_ADD_DELAY_ENABLE1 0xc71018 +#define CX18_ADD_DELAY_ENABLE2 0xc7101C +#define CX18_CLOCK_ENABLE1 0xc71020 +#define CX18_CLOCK_ENABLE2 0xc71024 + +#define CX18_REG_BUS_TIMEOUT_EN 0xc72024 + +#define CX18_AUDIO_ENABLE 0xc72014 +#define CX18_REG_BUS_TIMEOUT_EN 0xc72024 + +#define CX18_FAST_CLOCK_PLL_INT 0xc78000 +#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 +#define CX18_FAST_CLOCK_PLL_POST 0xc78008 +#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C +#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 + +#define CX18_SLOW_CLOCK_PLL_INT 0xc78014 +#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 +#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C +#define CX18_MPEG_CLOCK_PLL_INT 0xc78040 +#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 +#define CX18_MPEG_CLOCK_PLL_POST 0xc78048 +#define CX18_PLL_POWER_DOWN 0xc78088 +#define CX18_SW1_INT_STATUS 0xc73104 +#define CX18_SW1_INT_ENABLE_PCI 0xc7311C +#define CX18_SW2_INT_SET 0xc73140 +#define CX18_SW2_INT_STATUS 0xc73144 +#define CX18_ADEC_CONTROL 0xc78120 + +#define CX18_DDR_REQUEST_ENABLE 0xc80000 +#define CX18_DDR_CHIP_CONFIG 0xc80004 +#define CX18_DDR_REFRESH 0xc80008 +#define CX18_DDR_TIMING1 0xc8000C +#define CX18_DDR_TIMING2 0xc80010 +#define CX18_DDR_POWER_REG 0xc8001C + +#define CX18_DDR_TUNE_LANE 0xc80048 +#define CX18_DDR_INITIAL_EMRS 0xc80054 +#define CX18_DDR_MB_PER_ROW_7 0xc8009C +#define CX18_DDR_BASE_63_ADDR 0xc804FC + +#define CX18_WMB_CLIENT02 0xc90108 +#define CX18_WMB_CLIENT05 0xc90114 +#define CX18_WMB_CLIENT06 0xc90118 +#define CX18_WMB_CLIENT07 0xc9011C +#define CX18_WMB_CLIENT08 0xc90120 +#define CX18_WMB_CLIENT09 0xc90124 +#define CX18_WMB_CLIENT10 0xc90128 +#define CX18_WMB_CLIENT11 0xc9012C +#define CX18_WMB_CLIENT12 0xc90130 +#define CX18_WMB_CLIENT13 0xc90134 +#define CX18_WMB_CLIENT14 0xc90138 + +#define CX18_DSP0_INTERRUPT_MASK 0xd0004C + +/* Encoder/decoder firmware sizes */ +#define CX18_FW_CPU_SIZE (174716) +#define CX18_FW_APU_SIZE (141200) + +#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ +#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ + +struct cx18_apu_rom_seghdr { + u32 sync1; + u32 sync2; + u32 addr; + u32 size; +}; + +static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx, long size) +{ + const struct firmware *fw = NULL; + int retries = 3; + int i, j; + u32 __iomem *dst = (u32 __iomem *)mem; + const u32 *src; + +retry: + if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) { + CX18_ERR("Unable to open firmware %s (must be %ld bytes)\n", + fn, size); + CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); + return -ENOMEM; + } + + src = (const u32 *)fw->data; + + if (fw->size != size) { + /* Due to race conditions in firmware loading (esp. with + udev <0.95) the wrong file was sometimes loaded. So we check + filesizes to see if at least the right-sized file was + loaded. If not, then we retry. */ + CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", + fn, size, fw->size); + release_firmware(fw); + retries--; + goto retry; + } + for (i = 0; i < fw->size; i += 4096) { + setup_page(i); + for (j = i; j < fw->size && j < i + 4096; j += 4) { + /* no need for endianness conversion on the ppc */ + __raw_writel(*src, dst); + if (__raw_readl(dst) != *src) { + CX18_ERR("Mismatch at offset %x\n", i); + release_firmware(fw); + return -EIO; + } + dst++; + src++; + } + } + if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) + CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + release_firmware(fw); + return size; +} + +static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, long size) +{ + const struct firmware *fw = NULL; + int retries = 3; + int i, j; + const u32 *src; + struct cx18_apu_rom_seghdr seghdr; + const u8 *vers; + u32 offset = 0; + u32 apu_version = 0; + int sz; + +retry: + if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) { + CX18_ERR("unable to open firmware %s (must be %ld bytes)\n", + fn, size); + CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); + return -ENOMEM; + } + + src = (const u32 *)fw->data; + vers = fw->data + sizeof(seghdr); + sz = fw->size; + + if (fw->size != size) { + /* Due to race conditions in firmware loading (esp. with + udev <0.95) the wrong file was sometimes loaded. So we check + filesizes to see if at least the right-sized file was + loaded. If not, then we retry. */ + CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", + fn, size, fw->size); + release_firmware(fw); + retries--; + goto retry; + } + apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; + while (offset + sizeof(seghdr) < size) { + /* TODO: byteswapping */ + memcpy(&seghdr, src + offset / 4, sizeof(seghdr)); + offset += sizeof(seghdr); + if (seghdr.sync1 != APU_ROM_SYNC1 || + seghdr.sync2 != APU_ROM_SYNC2) { + offset += seghdr.size; + continue; + } + CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, + seghdr.addr + seghdr.size - 1); + if (offset + seghdr.size > sz) + break; + for (i = 0; i < seghdr.size; i += 4096) { + setup_page(offset + i); + for (j = i; j < seghdr.size && j < i + 4096; j += 4) { + /* no need for endianness conversion on the ppc */ + __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j); + if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) { + CX18_ERR("Mismatch at offset %x\n", offset + j); + release_firmware(fw); + return -EIO; + } + } + } + offset += seghdr.size; + } + if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) + CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", + fn, apu_version, fw->size); + release_firmware(fw); + /* Clear bit0 for APU to start from 0 */ + write_reg(read_reg(0xc72030) & ~1, 0xc72030); + return size; +} + +void cx18_halt_firmware(struct cx18 *cx) +{ + CX18_DEBUG_INFO("Preparing for firmware halt.\n"); + write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + write_reg(0x00020002, CX18_ADEC_CONTROL); +} + +void cx18_init_power(struct cx18 *cx, int lowpwr) +{ + /* power-down Spare and AOM PLLs */ + /* power-up fast, slow and mpeg PLLs */ + write_reg(0x00000008, CX18_PLL_POWER_DOWN); + + /* ADEC out of sleep */ + write_reg(0x00020000, CX18_ADEC_CONTROL); + + /* The fast clock is at 200/245 MHz */ + write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); + write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC); + + write_reg(2, CX18_FAST_CLOCK_PLL_POST); + write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE); + write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); + + /* set slow clock to 125/120 MHz */ + write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); + write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC); + write_reg(4, CX18_SLOW_CLOCK_PLL_POST); + + /* mpeg clock pll 54MHz */ + write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT); + write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); + write_reg(8, CX18_MPEG_CLOCK_PLL_POST); + + /* Defaults */ + /* APU = SC or SC/2 = 125/62.5 */ + /* EPU = SC = 125 */ + /* DDR = FC = 180 */ + /* ENC = SC = 125 */ + /* AI1 = SC = 125 */ + /* VIM2 = disabled */ + /* PCI = FC/2 = 90 */ + /* AI2 = disabled */ + /* DEMUX = disabled */ + /* AO = SC/2 = 62.5 */ + /* SER = 54MHz */ + /* VFC = disabled */ + /* USB = disabled */ + + write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1); + write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2); + + write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1); + write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2); + + write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1); + write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2); +} + +void cx18_init_memory(struct cx18 *cx) +{ + cx18_msleep_timeout(10, 0); + write_reg(0x10000, CX18_DDR_SOFT_RESET); + cx18_msleep_timeout(10, 0); + + write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); + + cx18_msleep_timeout(10, 0); + + write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH); + write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1); + write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2); + + cx18_msleep_timeout(10, 0); + + /* Initialize DQS pad time */ + write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); + write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); + + cx18_msleep_timeout(10, 0); + + write_reg(0x20000, CX18_DDR_SOFT_RESET); + cx18_msleep_timeout(10, 0); + + /* use power-down mode when idle */ + write_reg(0x00000010, CX18_DDR_POWER_REG); + + write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN); + + write_reg(0x48, CX18_DDR_MB_PER_ROW_7); + write_reg(0xE0000, CX18_DDR_BASE_63_ADDR); + + write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */ + write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */ + write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ + write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */ + write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ + write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */ + write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */ + write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */ + write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */ + write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */ +} + +int cx18_firmware_init(struct cx18 *cx) +{ + /* Allow chip to control CLKRUN */ + write_reg(0x5, CX18_DSP0_INTERRUPT_MASK); + + write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + + cx18_msleep_timeout(1, 0); + + sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); + sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + + /* Only if the processor is not running */ + if (read_reg(CX18_PROC_SOFT_RESET) & 8) { + int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", + cx->enc_mem, cx, CX18_FW_APU_SIZE); +#if 0 + /* this might be needed after all, check later */ + write_enc(0xE51FF004, 0); + write_enc(0xa00000, 4); /* todo: not hardcoded */ + write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */ + cx18_msleep_timeout(500, 0); +#endif + + sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", + cx->enc_mem, cx, CX18_FW_CPU_SIZE); + + if (sz > 0) { + int retries = 0; + + /* start the CPU */ + write_reg(0x00080000, CX18_PROC_SOFT_RESET); + while (retries++ < 50) { /* Loop for max 500mS */ + if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0) + break; + cx18_msleep_timeout(10, 0); + } + cx18_msleep_timeout(200, 0); + if (retries == 51) { + CX18_ERR("Could not start the CPU\n"); + return -EIO; + } + } + if (sz <= 0) + return -EIO; + } + /* initialize GPIO */ + write_reg(0x14001400, 0xC78110); + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-firmware.h b/linux/drivers/media/video/cx18/cx18-firmware.h new file mode 100644 index 000000000..38d4c05e8 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-firmware.h @@ -0,0 +1,25 @@ +/* + * cx18 firmware functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int cx18_firmware_init(struct cx18 *cx); +void cx18_halt_firmware(struct cx18 *cx); +void cx18_init_memory(struct cx18 *cx); +void cx18_init_power(struct cx18 *cx, int lowpwr); diff --git a/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c new file mode 100644 index 000000000..2f324b846 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-gpio.c @@ -0,0 +1,100 @@ +/* + * cx18 gpio functions + * + * Derived from ivtv-gpio.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-cards.h" +#include "cx18-gpio.h" +#include "tuner-xc2028.h" + +/********************* GPIO stuffs *********************/ + +/* GPIO registers */ +#define CX18_REG_GPIO_IN 0xc72010 +#define CX18_REG_GPIO_OUT1 0xc78100 +#define CX18_REG_GPIO_DIR1 0xc78108 +#define CX18_REG_GPIO_OUT2 0xc78104 +#define CX18_REG_GPIO_DIR2 0xc7810c + +/* + * HVR-1600 GPIO pins, courtesy of Hauppauge: + * + * gpio0: zilog ir process reset pin + * gpio1: zilog programming pin (you should never use this) + * gpio12: cx24227 reset pin + * gpio13: cs5345 reset pin +*/ + +static void gpio_write(struct cx18 *cx) +{ + u32 dir = cx->gpio_dir; + u32 val = cx->gpio_val; + + write_reg((dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); + write_reg(((dir & 0xffff) << 16) | (val & 0xffff), + CX18_REG_GPIO_OUT1); + write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2); + write_reg((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), + CX18_REG_GPIO_OUT2); +} + +void cx18_gpio_init(struct cx18 *cx) +{ + cx->gpio_dir = cx->card->gpio_init.direction; + cx->gpio_val = cx->card->gpio_init.initial_value; + + if (cx->card->xceive_pin) { + cx->gpio_dir |= 1 << cx->card->xceive_pin; + cx->gpio_val |= 1 << cx->card->xceive_pin; + } + + if (cx->gpio_dir == 0) + return; + + CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", + read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2), + read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2)); + + gpio_write(cx); +} + +/* Xceive tuner reset function */ +int cx18_reset_tuner_gpio(void *dev, int cmd, int value) +{ + struct i2c_algo_bit_data *algo = dev; + struct cx18_i2c_algo_callback_data *cb_data = algo->data; + struct cx18 *cx = cb_data->cx; + + if (cmd != XC2028_TUNER_RESET) + return 0; + CX18_DEBUG_INFO("Resetting tuner\n"); + + cx->gpio_val &= ~(1 << cx->card->xceive_pin); + + gpio_write(cx); + schedule_timeout_interruptible(msecs_to_jiffies(1)); + + cx->gpio_val |= 1 << cx->card->xceive_pin; + gpio_write(cx); + schedule_timeout_interruptible(msecs_to_jiffies(1)); + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-gpio.h b/linux/drivers/media/video/cx18/cx18-gpio.h new file mode 100644 index 000000000..41bac8856 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-gpio.h @@ -0,0 +1,24 @@ +/* + * cx18 gpio functions + * + * Derived from ivtv-gpio.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void cx18_gpio_init(struct cx18 *cx); +int cx18_reset_tuner_gpio(void *dev, int cmd, int value); diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c new file mode 100644 index 000000000..940e3718d --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -0,0 +1,461 @@ +/* + * cx18 I2C functions + * + * Derived from ivtv-i2c.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-cards.h" +#include "cx18-gpio.h" +#include "cx18-av-core.h" +#include "cx18-i2c.h" + +#include <media/ir-kbd-i2c.h> + +#define CX18_REG_I2C_1_WR 0xf15000 +#define CX18_REG_I2C_1_RD 0xf15008 +#define CX18_REG_I2C_2_WR 0xf25100 +#define CX18_REG_I2C_2_RD 0xf25108 + +#define SETSCL_BIT 0x0001 +#define SETSDL_BIT 0x0002 +#define GETSCL_BIT 0x0004 +#define GETSDL_BIT 0x0008 + +#ifndef I2C_ADAP_CLASS_TV_ANALOG +#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG +#endif + +#define CX18_CS5345_I2C_ADDR 0x4c + +/* This array should match the CX18_HW_ defines */ +static const u8 hw_driverids[] = { + I2C_DRIVERID_TUNER, + I2C_DRIVERID_TVEEPROM, + I2C_DRIVERID_CS5345, + 0, /* CX18_HW_GPIO dummy driver ID */ + 0 /* CX18_HW_CX23418 dummy driver ID */ +}; + +/* This array should match the CX18_HW_ defines */ +static const u8 hw_addrs[] = { + 0, + 0, + CX18_CS5345_I2C_ADDR, + 0, /* CX18_HW_GPIO dummy driver ID */ + 0, /* CX18_HW_CX23418 dummy driver ID */ +}; + +/* This array should match the CX18_HW_ defines */ +/* This might well become a card-specific array */ +static const u8 hw_bus[] = { + 0, + 0, + 0, + 0, /* CX18_HW_GPIO dummy driver ID */ + 0, /* CX18_HW_CX23418 dummy driver ID */ +}; + +/* This array should match the CX18_HW_ defines */ +static const char * const hw_drivernames[] = { + "tuner", + "tveeprom", + "cs5345", + "gpio", + "cx23418", +}; + +int cx18_i2c_register(struct cx18 *cx, unsigned idx) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + struct i2c_board_info info; + struct i2c_client *c; + u8 id, bus; + int i; + + CX18_DEBUG_I2C("i2c client register\n"); + if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) + return -1; + id = hw_driverids[idx]; + bus = hw_bus[idx]; + memset(&info, 0, sizeof(info)); + strlcpy(info.driver_name, hw_drivernames[idx], + sizeof(info.driver_name)); + info.addr = hw_addrs[idx]; + for (i = 0; i < I2C_CLIENTS_MAX; i++) + if (cx->i2c_clients[i] == NULL) + break; + + if (i == I2C_CLIENTS_MAX) { + CX18_ERR("insufficient room for new I2C client!\n"); + return -ENOMEM; + } + + if (id != I2C_DRIVERID_TUNER) { + c = i2c_new_device(&cx->i2c_adap[bus], &info); + if (c->driver == NULL) + i2c_unregister_device(c); + else + cx->i2c_clients[i] = c; + return cx->i2c_clients[i] ? 0 : -ENODEV; + } + + /* special tuner handling */ + c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio); + if (c && c->driver == NULL) + i2c_unregister_device(c); + else if (c) + cx->i2c_clients[i++] = c; + c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod); + if (c && c->driver == NULL) + i2c_unregister_device(c); + else if (c) + cx->i2c_clients[i++] = c; + c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv); + if (c && c->driver == NULL) + i2c_unregister_device(c); + else if (c) + cx->i2c_clients[i++] = c; + return 0; +#else + return 0; +#endif +} + +static int attach_inform(struct i2c_client *client) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); + int i; + + CX18_DEBUG_I2C("i2c client attach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (cx->i2c_clients[i] == NULL) { + cx->i2c_clients[i] = client; + break; + } + } + if (i == I2C_CLIENTS_MAX) + CX18_ERR("Insufficient room for new I2C client\n"); +#endif + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + int i; + struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); + + CX18_DEBUG_I2C("i2c client detach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (cx->i2c_clients[i] == client) { + cx->i2c_clients[i] = NULL; + break; + } + } + CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n", + client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + + return 0; +} + +static void cx18_setscl(void *data, int state) +{ + struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; + int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; + u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; + u32 r = read_reg(addr); + + if (state) + write_reg_sync(r | SETSCL_BIT, addr); + else + write_reg_sync(r & ~SETSCL_BIT, addr); +} + +static void cx18_setsda(void *data, int state) +{ + struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; + int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; + u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; + u32 r = read_reg(addr); + + if (state) + write_reg_sync(r | SETSDL_BIT, addr); + else + write_reg_sync(r & ~SETSDL_BIT, addr); +} + +static int cx18_getscl(void *data) +{ + struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; + int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; + u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; + + return read_reg(addr) & GETSCL_BIT; +} + +static int cx18_getsda(void *data) +{ + struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; + int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; + u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; + + return read_reg(addr) & GETSDL_BIT; +} + +/* template for i2c-bit-algo */ +static struct i2c_adapter cx18_i2c_adap_template = { + .name = "cx18 i2c driver", + .id = I2C_HW_B_CX2341X, + .algo = NULL, /* set by i2c-algo-bit */ + .algo_data = NULL, /* filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +#endif +}; + +#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */ +#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */ + +static struct i2c_algo_bit_data cx18_i2c_algo_template = { + .setsda = cx18_setsda, + .setscl = cx18_setscl, + .getsda = cx18_getsda, + .getscl = cx18_getscl, + .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/ + .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ +}; + +static struct i2c_client cx18_i2c_client_template = { + .name = "cx18 internal", +}; + +int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg) +{ + struct i2c_client *client; + int retval; + int i; + + CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = cx->i2c_clients[i]; + if (client == NULL || client->driver == NULL || + client->driver->command == NULL) + continue; + if (addr == client->addr) { + retval = client->driver->command(client, cmd, arg); + return retval; + } + } + if (cmd != VIDIOC_G_CHIP_IDENT) + CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n", + addr, cmd); + return -ENODEV; +} + +/* Find the i2c device based on the driver ID and return + its i2c address or -ENODEV if no matching device was found. */ +static int cx18_i2c_id_addr(struct cx18 *cx, u32 id) +{ + struct i2c_client *client; + int retval = -ENODEV; + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = cx->i2c_clients[i]; + if (client == NULL || client->driver == NULL) + continue; + if (id == client->driver->id) { + retval = client->addr; + break; + } + } + return retval; +} + +/* Find the i2c device name matching the DRIVERID */ +static const char *cx18_i2c_id_name(u32 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (hw_driverids[i] == id) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device name matching the CX18_HW_ flag */ +static const char *cx18_i2c_hw_name(u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device matching the CX18_HW_ flag and return + its i2c address or -ENODEV if no matching device was found. */ +int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return cx18_i2c_id_addr(cx, hw_driverids[i]); + return -ENODEV; +} + +/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing. + If hw == CX18_HW_GPIO then call the gpio handler. */ +int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg) +{ + int addr; + + if (hw == CX18_HW_GPIO || hw == 0) + return 0; + if (hw == CX18_HW_CX23418) + return cx18_av_cmd(cx, cmd, arg); + + addr = cx18_i2c_hw_addr(cx, hw); + if (addr < 0) { + CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n", + hw, cx18_i2c_hw_name(hw), cmd); + return addr; + } + return cx18_call_i2c_client(cx, addr, cmd, arg); +} + +/* Calls i2c device based on I2C driver ID. */ +int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg) +{ + int addr; + + addr = cx18_i2c_id_addr(cx, id); + if (addr < 0) { + if (cmd != VIDIOC_G_CHIP_IDENT) + CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n", + id, cx18_i2c_id_name(id), cmd); + return addr; + } + return cx18_call_i2c_client(cx, addr, cmd, arg); +} + +/* broadcast cmd for all I2C clients and for the gpio subsystem */ +void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg) +{ + if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) { + CX18_ERR("adapter is not set\n"); + return; + } + cx18_av_cmd(cx, cmd, arg); + i2c_clients_command(&cx->i2c_adap[0], cmd, arg); + i2c_clients_command(&cx->i2c_adap[1], cmd, arg); +} + +/* init + register i2c algo-bit adapter */ +int init_cx18_i2c(struct cx18 *cx) +{ + int i; + CX18_DEBUG_I2C("i2c init\n"); + + for (i = 0; i < 2; i++) { + memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, + sizeof(struct i2c_adapter)); + memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, + sizeof(struct i2c_algo_bit_data)); + cx->i2c_algo_cb_data[i].cx = cx; + cx->i2c_algo_cb_data[i].bus_index = i; + cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; + cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; + + sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), + " #%d-%d", cx->num, i); + i2c_set_adapdata(&cx->i2c_adap[i], cx); + + memcpy(&cx->i2c_client[i], &cx18_i2c_client_template, + sizeof(struct i2c_client)); + sprintf(cx->i2c_client[i].name + + strlen(cx->i2c_client[i].name), "%d", i); + cx->i2c_client[i].adapter = &cx->i2c_adap[i]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + cx->i2c_adap[i].dev.parent = &cx->dev->dev; +#endif + } + + if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) { + /* Reset/Unreset I2C hardware block */ + write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */ + write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */ + } + /* courtesy of Steven Toth <stoth@hauppauge.com> */ + write_reg_sync(0x00c00000, 0xc7001c); + mdelay(10); + write_reg_sync(0x00c000c0, 0xc7001c); + mdelay(10); + write_reg_sync(0x00c00000, 0xc7001c); + + write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */ + write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */ + + /* Hw I2C1 Clock Freq ~100kHz */ + write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR); + cx18_setscl(&cx->i2c_algo_cb_data[0], 1); + cx18_setsda(&cx->i2c_algo_cb_data[0], 1); + + /* Hw I2C2 Clock Freq ~100kHz */ + write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR); + cx18_setscl(&cx->i2c_algo_cb_data[1], 1); + cx18_setsda(&cx->i2c_algo_cb_data[1], 1); + + return i2c_bit_add_bus(&cx->i2c_adap[0]) || + i2c_bit_add_bus(&cx->i2c_adap[1]); +} + +void exit_cx18_i2c(struct cx18 *cx) +{ + int i; + CX18_DEBUG_I2C("i2c exit\n"); + write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR); + write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR); + + for (i = 0; i < 2; i++) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + i2c_del_adapter(&cx->i2c_adap[i]); +#else + i2c_bit_del_bus(&cx->i2c_adap[i]); +#endif + } +} + +/* + Hauppauge HVR1600 should have: + 32 cx24227 + 98 unknown + a0 eeprom + c2 tuner + e? zilog ir + */ diff --git a/linux/drivers/media/video/cx18/cx18-i2c.h b/linux/drivers/media/video/cx18/cx18-i2c.h new file mode 100644 index 000000000..113c3f9a2 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-i2c.h @@ -0,0 +1,33 @@ +/* + * cx18 I2C functions + * + * Derived from ivtv-i2c.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw); +int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg); +int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg); +int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg); +void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg); +int cx18_i2c_register(struct cx18 *cx, unsigned idx); + +/* init + register i2c algo-bit adapter */ +int init_cx18_i2c(struct cx18 *cx); +void exit_cx18_i2c(struct cx18 *cx); diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c new file mode 100644 index 000000000..f008604f9 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -0,0 +1,878 @@ +/* + * cx18 ioctl system call + * + * Derived from ivtv-ioctl.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-version.h" +#include "cx18-mailbox.h" +#include "cx18-i2c.h" +#include "cx18-queue.h" +#include "cx18-fileops.h" +#include "cx18-vbi.h" +#include "cx18-audio.h" +#include "cx18-video.h" +#include "cx18-streams.h" +#include "cx18-ioctl.h" +#include "cx18-gpio.h" +#include "cx18-controls.h" +#include "cx18-cards.h" +#include "cx18-av-core.h" +#include <media/tveeprom.h> +#include <media/v4l2-chip-ident.h> +#include <linux/i2c-id.h> + +u16 cx18_service2vbi(int type) +{ + switch (type) { + case V4L2_SLICED_TELETEXT_B: + return CX18_SLICED_TYPE_TELETEXT_B; + case V4L2_SLICED_CAPTION_525: + return CX18_SLICED_TYPE_CAPTION_525; + case V4L2_SLICED_WSS_625: + return CX18_SLICED_TYPE_WSS_625; + case V4L2_SLICED_VPS: + return CX18_SLICED_TYPE_VPS; + default: + return 0; + } +} + +static int valid_service_line(int field, int line, int is_pal) +{ + return (is_pal && line >= 6 && (line != 23 || field == 0)) || + (!is_pal && line >= 10 && line < 22); +} + +static u16 select_service_from_set(int field, int line, u16 set, int is_pal) +{ + u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); + int i; + + set = set & valid_set; + if (set == 0 || !valid_service_line(field, line, is_pal)) + return 0; + if (!is_pal) { + if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) + return V4L2_SLICED_CAPTION_525; + } else { + if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) + return V4L2_SLICED_VPS; + if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) + return V4L2_SLICED_WSS_625; + if (line == 23) + return 0; + } + for (i = 0; i < 32; i++) { + if ((1 << i) & set) + return 1 << i; + } + return 0; +} + +void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + u16 set = fmt->service_set; + int f, l; + + fmt->service_set = 0; + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) + fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); + } +} + +static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); + set |= fmt->service_lines[f][l]; + } + } + return set != 0; +} + +u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) + set |= fmt->service_lines[f][l]; + } + return set; +} + +static const struct { + v4l2_std_id std; + char *name; +} enum_stds[] = { + { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-L'" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-J" }, + { V4L2_STD_NTSC_M_KR, "NTSC-K" }, +}; + +static const struct v4l2_standard cx18_std_60hz = { + .frameperiod = {.numerator = 1001, .denominator = 30000}, + .framelines = 525, +}; + +static const struct v4l2_standard cx18_std_50hz = { + .frameperiod = { .numerator = 1, .denominator = 25 }, + .framelines = 625, +}; + +static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) +{ + struct v4l2_register *regs = arg; + unsigned long flags; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) + return -EINVAL; + + spin_lock_irqsave(&cx18_cards_lock, flags); + if (cmd == VIDIOC_DBG_G_REGISTER) + regs->val = read_enc(regs->reg); + else + write_enc(regs->val, regs->reg); + spin_unlock_irqrestore(&cx18_cards_lock, flags); + return 0; +} + +static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt) +{ + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt->fmt.pix.width = cx->params.width; + fmt->fmt.pix.height = cx->params.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (streamtype == CX18_ENC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VBI_CAPTURE: + fmt->fmt.vbi.sampling_rate = 27000000; + fmt->fmt.vbi.offset = 248; + fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4; + fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + fmt->fmt.vbi.start[0] = cx->vbi.start[0]; + fmt->fmt.vbi.start[1] = cx->vbi.start[1]; + fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count; + break; + + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + + cx18_av_cmd(cx, VIDIOC_G_FMT, fmt); + vbifmt->service_set = cx18_get_service_set(vbifmt); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype, + struct v4l2_format *fmt, int set_fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + u16 set; + + /* set window size */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + if (w > 720) + w = 720; + else if (w < 1) + w = 1; + if (h > (cx->is_50hz ? 576 : 480)) + h = (cx->is_50hz ? 576 : 480); + else if (h < 2) + h = 2; + cx18_get_fmt(cx, streamtype, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) + if (cx->params.width != 720 || cx->params.height != (cx->is_50hz ? 576 : 480)) + cx->params.video_temporal_filter = 0; + else + cx->params.video_temporal_filter = 8; +#endif + + if (!set_fmt || (cx->params.width == w && cx->params.height == h)) + return 0; + if (atomic_read(&cx->ana_capturing) > 0) + return -EBUSY; + + cx->params.width = w; + cx->params.height = h; + if (w != 720 || h != (cx->is_50hz ? 576 : 480)) + cx->params.video_temporal_filter = 0; + else + cx->params.video_temporal_filter = 8; + cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + return cx18_get_fmt(cx, streamtype, fmt); + } + + /* set raw VBI format */ + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI && + cx->vbi.sliced_in->service_set && + atomic_read(&cx->ana_capturing) > 0) + return -EBUSY; + if (set_fmt) { + cx->vbi.sliced_in->service_set = 0; + cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); + } + return cx18_get_fmt(cx, streamtype, fmt); + } + + /* any else but sliced VBI capture is an error */ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + /* TODO: implement sliced VBI, for now silently return 0 */ + return 0; + + /* set sliced VBI capture format */ + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + + if (vbifmt->service_set) + cx18_expand_service_set(vbifmt, cx->is_50hz); + set = check_service_set(vbifmt, cx->is_50hz); + vbifmt->service_set = cx18_get_service_set(vbifmt); + + if (!set_fmt) + return 0; + if (set == 0) + return -EINVAL; + if (atomic_read(&cx->ana_capturing) > 0 && cx->vbi.sliced_in->service_set == 0) + return -EBUSY; + cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); + return 0; +} + +static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; + struct cx18 *cx = id->cx; + struct v4l2_register *reg = arg; + + switch (cmd) { + /* ioctls to allow direct access to the encoder registers for testing */ + case VIDIOC_DBG_G_REGISTER: + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return cx18_cxc(cx, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return cx18_i2c_id(cx, reg->match_chip, cmd, arg); + return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg); + + case VIDIOC_DBG_S_REGISTER: + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return cx18_cxc(cx, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return cx18_i2c_id(cx, reg->match_chip, cmd, arg); + return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg); + + case VIDIOC_G_CHIP_IDENT: { + struct v4l2_chip_ident *chip = arg; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (reg->match_type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) { + struct v4l2_chip_ident *chip = arg; + + chip->ident = V4L2_IDENT_CX23418; + } + return 0; + } + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return cx18_i2c_id(cx, reg->match_chip, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR) + return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg); + return -EINVAL; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + cx18_audio_set_route(cx, route); + break; + } + + default: + return -EINVAL; + } + return 0; +} + +int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg) +{ + struct cx18_open_id *id = NULL; + + if (filp) + id = (struct cx18_open_id *)filp->private_data; + + switch (cmd) { + case VIDIOC_G_PRIORITY: + { + enum v4l2_priority *p = arg; + + *p = v4l2_prio_max(&cx->prio); + break; + } + + case VIDIOC_S_PRIORITY: + { + enum v4l2_priority *prio = arg; + + return v4l2_prio_change(&cx->prio, &id->prio, *prio); + } + + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *vcap = arg; + + memset(vcap, 0, sizeof(*vcap)); + strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); + strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); + strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info)); + vcap->version = CX18_DRIVER_VERSION; /* version */ + vcap->capabilities = cx->v4l2_cap; /* capabilities */ + + /* reserved.. must set to 0! */ + vcap->reserved[0] = vcap->reserved[1] = + vcap->reserved[2] = vcap->reserved[3] = 0; + break; + } + + case VIDIOC_ENUMAUDIO:{ + struct v4l2_audio *vin = arg; + + return cx18_get_audio_input(cx, vin->index, vin); + } + + case VIDIOC_G_AUDIO:{ + struct v4l2_audio *vin = arg; + + vin->index = cx->audio_input; + return cx18_get_audio_input(cx, vin->index, vin); + } + + case VIDIOC_S_AUDIO:{ + struct v4l2_audio *vout = arg; + + if (vout->index >= cx->nof_audio_inputs) + return -EINVAL; + cx->audio_input = vout->index; + cx18_audio_set_io(cx); + break; + } + + case VIDIOC_ENUMINPUT:{ + struct v4l2_input *vin = arg; + + /* set it to defaults from our table */ + return cx18_get_input(cx, vin->index, vin); + } + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: { + struct v4l2_format *fmt = arg; + + return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT); + } + + case VIDIOC_G_FMT: { + struct v4l2_format *fmt = arg; + int type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->type = type; + return cx18_get_fmt(cx, id->type, fmt); + } + + case VIDIOC_CROPCAP: { + struct v4l2_cropcap *cropcap = arg; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = cx->is_50hz ? 576 : 480; + cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; + cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; + cropcap->defrect = cropcap->bounds; + return 0; + } + + case VIDIOC_S_CROP: { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return cx18_av_cmd(cx, VIDIOC_S_CROP, arg); + } + + case VIDIOC_G_CROP: { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return cx18_av_cmd(cx, VIDIOC_G_CROP, arg); + } + + case VIDIOC_ENUM_FMT: { + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, + { 0, 0, 0, 0 } + }, + { 1, 0, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, + { 0, 0, 0, 0 } + } + }; + struct v4l2_fmtdesc *fmt = arg; + enum v4l2_buf_type type = fmt->type; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + default: + return -EINVAL; + } + if (fmt->index > 1) + return -EINVAL; + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; + } + + case VIDIOC_G_INPUT:{ + *(int *)arg = cx->active_input; + break; + } + + case VIDIOC_S_INPUT:{ + int inp = *(int *)arg; + + if (inp < 0 || inp >= cx->nof_inputs) + return -EINVAL; + + if (inp == cx->active_input) { + CX18_DEBUG_INFO("Input unchanged\n"); + break; + } + CX18_DEBUG_INFO("Changing input from %d to %d\n", + cx->active_input, inp); + + cx->active_input = inp; + /* Set the audio input to whatever is appropriate for the + input type. */ + cx->audio_input = cx->card->video_inputs[inp].audio_index; + + /* prevent others from messing with the streams until + we're finished changing inputs. */ + cx18_mute(cx); + cx18_video_set_io(cx); + cx18_audio_set_io(cx); + cx18_unmute(cx); + break; + } + + case VIDIOC_G_FREQUENCY:{ + struct v4l2_frequency *vf = arg; + + if (vf->tuner != 0) + return -EINVAL; + cx18_call_i2c_clients(cx, cmd, arg); + break; + } + + case VIDIOC_S_FREQUENCY:{ + struct v4l2_frequency vf = *(struct v4l2_frequency *)arg; + + if (vf.tuner != 0) + return -EINVAL; + + cx18_mute(cx); + CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency); + cx18_call_i2c_clients(cx, cmd, &vf); + cx18_unmute(cx); + break; + } + + case VIDIOC_ENUMSTD:{ + struct v4l2_standard *vs = arg; + int idx = vs->index; + + if (idx < 0 || idx >= ARRAY_SIZE(enum_stds)) + return -EINVAL; + + *vs = (enum_stds[idx].std & V4L2_STD_525_60) ? + cx18_std_60hz : cx18_std_50hz; + vs->index = idx; + vs->id = enum_stds[idx].std; + strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name)); + break; + } + + case VIDIOC_G_STD:{ + *(v4l2_std_id *) arg = cx->std; + break; + } + + case VIDIOC_S_STD: { + v4l2_std_id std = *(v4l2_std_id *) arg; + + if ((std & V4L2_STD_ALL) == 0) + return -EINVAL; + + if (std == cx->std) + break; + + if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || + atomic_read(&cx->ana_capturing) > 0) { + /* Switching standard would turn off the radio or mess + with already running streams, prevent that by + returning EBUSY. */ + return -EBUSY; + } + + cx->std = std; + cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; + cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; + cx->params.width = 720; + cx->params.height = cx->is_50hz ? 576 : 480; + cx->vbi.count = cx->is_50hz ? 18 : 12; + cx->vbi.start[0] = cx->is_50hz ? 6 : 10; + cx->vbi.start[1] = cx->is_50hz ? 318 : 273; + cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; + CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std); + + /* Tuner */ + cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); + break; + } + + case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ + struct v4l2_tuner *vt = arg; + + if (vt->index != 0) + return -EINVAL; + + cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *vt = arg; + + if (vt->index != 0) + return -EINVAL; + + memset(vt, 0, sizeof(*vt)); + cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); + + if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { + strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); + vt->type = V4L2_TUNER_RADIO; + } else { + strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); + vt->type = V4L2_TUNER_ANALOG_TV; + } + break; + } + + case VIDIOC_G_SLICED_VBI_CAP: { + struct v4l2_sliced_vbi_cap *cap = arg; + int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; + int f, l; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) + enum v4l2_buf_type type = cap->type; +#else + enum v4l2_buf_type type = VIDIOC_G_SLICED_VBI_CAP; +#endif + + memset(cap, 0, sizeof(*cap)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) + cap->type = type; +#endif + if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, cx->is_50hz)) + cap->service_lines[f][l] = set; + } + } + return 0; + } + return -EINVAL; + } + +#if 0 + case VIDIOC_G_ENC_INDEX: { + struct v4l2_enc_idx *idx = arg; + int i; + + idx->entries = (cx->pgm_info_write_idx + CX18_MAX_PGM_INDEX - cx->pgm_info_read_idx) % + CX18_MAX_PGM_INDEX; + if (idx->entries > V4L2_ENC_IDX_ENTRIES) + idx->entries = V4L2_ENC_IDX_ENTRIES; + for (i = 0; i < idx->entries; i++) + idx->entry[i] = cx->pgm_info[(cx->pgm_info_read_idx + i) % CX18_MAX_PGM_INDEX]; + cx->pgm_info_read_idx = (cx->pgm_info_read_idx + idx->entries) % CX18_MAX_PGM_INDEX; + break; + } +#endif + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: { + struct v4l2_encoder_cmd *enc = arg; + int try = cmd == VIDIOC_TRY_ENCODER_CMD; + + memset(&enc->raw, 0, sizeof(enc->raw)); + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + enc->flags = 0; + if (try) + return 0; + return cx18_start_capture(id); + + case V4L2_ENC_CMD_STOP: + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + if (try) + return 0; + cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); + return 0; + + case V4L2_ENC_CMD_PAUSE: + enc->flags = 0; + if (try) + return 0; + if (!atomic_read(&cx->ana_capturing)) + return -EPERM; + if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) + return 0; + cx18_mute(cx); + cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx)); + break; + + case V4L2_ENC_CMD_RESUME: + enc->flags = 0; + if (try) + return 0; + if (!atomic_read(&cx->ana_capturing)) + return -EPERM; + if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) + return 0; + cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx)); + cx18_unmute(cx); + break; + default: + return -EINVAL; + } + break; + } + + case VIDIOC_LOG_STATUS: + { + struct v4l2_input vidin; + struct v4l2_audio audin; + int i; + + CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); + if (cx->hw_flags & CX18_HW_TVEEPROM) { + struct tveeprom tv; + + cx18_read_eeprom(cx, &tv); + } + cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); + cx18_get_input(cx, cx->active_input, &vidin); + cx18_get_audio_input(cx, cx->audio_input, &audin); + CX18_INFO("Video Input: %s\n", vidin.name); + CX18_INFO("Audio Input: %s\n", audin.name); + CX18_INFO("Tuner: %s\n", + test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? + "Radio" : "TV"); + cx2341x_log_status(&cx->params, cx->name); + CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); + for (i = 0; i < CX18_MAX_STREAMS; i++) { + struct cx18_stream *s = &cx->streams[i]; + + if (s->v4l2dev == NULL || s->buffers == 0) + continue; + CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", + s->name, s->s_flags, + (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers * s->buf_size) / 1024, s->buffers); + } + CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", + (long long)cx->mpg_data_received, + (long long)cx->vbi_data_inserted); + CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, void *arg) +{ + struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; + struct cx18 *cx = id->cx; + int ret; + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + case VIDIOC_S_FMT: + case VIDIOC_S_CROP: + case VIDIOC_S_EXT_CTRLS: + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; + } + + switch (cmd) { + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_INT_S_AUDIO_ROUTING: + case VIDIOC_INT_RESET: + if (cx18_debug & CX18_DBGFLG_IOCTL) { + printk(KERN_INFO "cx18%d ioctl: ", cx->num); + v4l_printk_ioctl(cmd); + } + return cx18_debug_ioctls(filp, cmd, arg); + + case VIDIOC_G_PRIORITY: + case VIDIOC_S_PRIORITY: + case VIDIOC_QUERYCAP: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_ENUM_FMT: + case VIDIOC_CROPCAP: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_S_TUNER: + case VIDIOC_G_TUNER: + case VIDIOC_ENUMAUDIO: + case VIDIOC_S_AUDIO: + case VIDIOC_G_AUDIO: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + if (cx18_debug & CX18_DBGFLG_IOCTL) { + printk(KERN_INFO "cx18%d ioctl: ", cx->num); + v4l_printk_ioctl(cmd); + } + return cx18_v4l2_ioctls(cx, filp, cmd, arg); + + case VIDIOC_QUERYMENU: + case VIDIOC_QUERYCTRL: + case VIDIOC_S_CTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + if (cx18_debug & CX18_DBGFLG_IOCTL) { + printk(KERN_INFO "cx18%d ioctl: ", cx->num); + v4l_printk_ioctl(cmd); + } + return cx18_control_ioctls(cx, cmd, arg); + + case 0x00005401: /* Handle isatty() calls */ + return -EINVAL; + default: + return v4l_compat_translate_ioctl(inode, filp, cmd, arg, + cx18_v4l2_do_ioctl); + } + return 0; +} + +int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; + struct cx18 *cx = id->cx; + int res; + + mutex_lock(&cx->serialize_lock); + res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl); + mutex_unlock(&cx->serialize_lock); + return res; +} diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.h b/linux/drivers/media/video/cx18/cx18-ioctl.h new file mode 100644 index 000000000..9f4c7eb28 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-ioctl.h @@ -0,0 +1,30 @@ +/* + * cx18 ioctl system call + * + * Derived from ivtv-ioctl.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +u16 cx18_service2vbi(int type); +void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); +u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt); +int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, + void *arg); diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c new file mode 100644 index 000000000..6e14f8bda --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -0,0 +1,179 @@ +/* + * cx18 interrupt handling + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-firmware.h" +#include "cx18-fileops.h" +#include "cx18-queue.h" +#include "cx18-irq.h" +#include "cx18-ioctl.h" +#include "cx18-mailbox.h" +#include "cx18-vbi.h" +#include "cx18-scb.h" + +#define DMA_MAGIC_COOKIE 0x000001fe + +static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) +{ + u32 handle = mb->args[0]; + struct cx18_stream *s = NULL; + struct cx18_buffer *buf; + u32 off; + int i; + int id; + + for (i = 0; i < CX18_MAX_STREAMS; i++) { + s = &cx->streams[i]; + if ((handle == s->handle) && (s->dvb.enabled)) + break; + if (s->v4l2dev && handle == s->handle) + break; + } + if (i == CX18_MAX_STREAMS) { + CX18_WARN("DMA done for unknown handle %d for stream %s\n", + handle, s->name); + mb->error = CXERR_NOT_OPEN; + mb->cmd = 0; + cx18_mb_ack(cx, mb); + return; + } + + off = mb->args[1]; + if (mb->args[2] != 1) + CX18_WARN("Ack struct = %d for %s\n", + mb->args[2], s->name); + id = read_enc(off); + buf = cx18_queue_find_buf(s, id, read_enc(off + 4)); + CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); + if (buf) { + cx18_buf_sync_for_cpu(s, buf); + if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { + /* process the buffer here */ + CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n", + buf->bytesused); + + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + buf->bytesused); + + cx18_buf_sync_for_device(s, buf); + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, + (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, + 1, buf->id, s->buf_size); + } else + set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); + } else { + CX18_WARN("Could not find buf %d for stream %s\n", + read_enc(off), s->name); + } + mb->error = 0; + mb->cmd = 0; + cx18_mb_ack(cx, mb); + wake_up(&cx->dma_waitq); + if (s->id != -1) + wake_up(&s->waitq); +} + +static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) +{ + char str[256] = { 0 }; + char *p; + + if (mb->args[1]) { + setup_page(mb->args[1]); + memcpy_fromio(str, cx->enc_mem + mb->args[1], 252); + str[252] = 0; + } + cx18_mb_ack(cx, mb); + CX18_DEBUG_INFO("%x %s\n", mb->args[0], str); + p = strchr(str, '.'); + if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) + CX18_INFO("FW version: %s\n", p - 1); +} + +static void hpu_cmd(struct cx18 *cx, u32 sw1) +{ + struct cx18_mailbox mb; + + if (sw1 & IRQ_CPU_TO_EPU) { + memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb)); + mb.error = 0; + + switch (mb.cmd) { + case CX18_EPU_DMA_DONE: + epu_dma_done(cx, &mb); + break; + case CX18_EPU_DEBUG: + epu_debug(cx, &mb); + break; + default: + CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd); + break; + } + } + if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU)) + CX18_WARN("Unexpected interrupt %08x\n", sw1); +} + +irqreturn_t cx18_irq_handler(int irq, void *dev_id) +{ + struct cx18 *cx = (struct cx18 *)dev_id; + u32 sw1, sw1_mask; + u32 sw2, sw2_mask; + u32 hw2, hw2_mask; + + spin_lock(&cx->dma_reg_lock); + + hw2_mask = read_reg(HW2_INT_MASK5_PCI); + hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask; + sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; + sw2 = read_reg(SW2_INT_STATUS) & sw2_mask; + sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; + sw1 = read_reg(SW1_INT_STATUS) & sw1_mask; + + write_reg(sw2&sw2_mask, SW2_INT_STATUS); + write_reg(sw1&sw1_mask, SW1_INT_STATUS); + write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS); + + if (sw1 || sw2 || hw2) + CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); + + /* To do: interrupt-based I2C handling + if (hw2 & 0x00c00000) { + } + */ + + if (sw2) { + if (sw2 & (cx->scb->cpu2hpu_irq_ack | cx->scb->cpu2epu_irq_ack)) + wake_up(&cx->mb_cpu_waitq); + if (sw2 & (cx->scb->apu2hpu_irq_ack | cx->scb->apu2epu_irq_ack)) + wake_up(&cx->mb_apu_waitq); + if (sw2 & cx->scb->epu2hpu_irq_ack) + wake_up(&cx->mb_epu_waitq); + if (sw2 & cx->scb->hpu2epu_irq_ack) + wake_up(&cx->mb_hpu_waitq); + } + + if (sw1) + hpu_cmd(cx, sw1); + spin_unlock(&cx->dma_reg_lock); + + return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE; +} diff --git a/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h new file mode 100644 index 000000000..379f704f5 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-irq.h @@ -0,0 +1,37 @@ +/* + * cx18 interrupt handling + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#define HW2_I2C1_INT (1 << 22) +#define HW2_I2C2_INT (1 << 23) +#define HW2_INT_CLR_STATUS 0xc730c4 +#define HW2_INT_MASK5_PCI 0xc730e4 +#define SW1_INT_SET 0xc73100 +#define SW1_INT_STATUS 0xc73104 +#define SW1_INT_ENABLE_PCI 0xc7311c +#define SW2_INT_SET 0xc73140 +#define SW2_INT_STATUS 0xc73144 +#define SW2_INT_ENABLE_PCI 0xc7315c + +irqreturn_t cx18_irq_handler(int irq, void *dev_id); + +void cx18_irq_work_handler(struct work_struct *work); +void cx18_dma_stream_dec_prepare(struct cx18_stream *s, u32 offset, int lock); +void cx18_unfinished_dma(unsigned long arg); diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c new file mode 100644 index 000000000..0c5f328bc --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -0,0 +1,372 @@ +/* + * cx18 mailbox functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include <stdarg.h> + +#include "cx18-driver.h" +#include "cx18-scb.h" +#include "cx18-irq.h" +#include "cx18-mailbox.h" + +#define API_FAST (1 << 2) /* Short timeout */ +#define API_SLOW (1 << 3) /* Additional 300ms timeout */ + +#define APU 0 +#define CPU 1 +#define EPU 2 +#define HPU 3 + +struct cx18_api_info { + u32 cmd; + u8 flags; /* Flags, see above */ + u8 rpu; /* Processing unit */ + const char *name; /* The name of the command */ +}; + +#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x } + +static const struct cx18_api_info api_info[] = { + /* MPEG encoder API */ + API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), + API_ENTRY(CPU, CX18_EPU_DEBUG, 0), + API_ENTRY(CPU, CX18_CREATE_TASK, 0), + API_ENTRY(CPU, CX18_DESTROY_TASK, 0), + API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW), + API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW), + API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0), + API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0), + API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), + API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0), + API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0), + API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0), + API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0), + API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0), + API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0), + API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0), + API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0), + API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0), + API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0), + API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0), + API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0), + API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW), + API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0), + API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0), + API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0), + API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0), + API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0), + API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0), + API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0), + API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0), + API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0), + API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), + API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), + API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), + API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), + API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), + API_ENTRY(0, 0, 0), +}; + +static const struct cx18_api_info *find_api_info(u32 cmd) +{ + int i; + + for (i = 0; api_info[i].cmd; i++) + if (api_info[i].cmd == cmd) + return &api_info[i]; + return NULL; +} + +static struct cx18_mailbox *cx18_mb_is_complete(struct cx18 *cx, int rpu, + u32 *state, u32 *irq, u32 *req) +{ + struct cx18_mailbox *mb = NULL; + int wait_count = 0; + u32 ack; + + switch (rpu) { + case APU: + mb = &cx->scb->epu2apu_mb; + *state = readl(&cx->scb->apu_state); + *irq = readl(&cx->scb->epu2apu_irq); + break; + + case CPU: + mb = &cx->scb->epu2cpu_mb; + *state = readl(&cx->scb->cpu_state); + *irq = readl(&cx->scb->epu2cpu_irq); + break; + + case HPU: + mb = &cx->scb->epu2hpu_mb; + *state = readl(&cx->scb->hpu_state); + *irq = readl(&cx->scb->epu2hpu_irq); + break; + } + + if (mb == NULL) + return mb; + + do { + *req = readl(&mb->request); + ack = readl(&mb->ack); + wait_count++; + } while (*req != ack && wait_count < 600); + + if (*req == ack) { + (*req)++; + if (*req == 0 || *req == 0xffffffff) + *req = 1; + return mb; + } + return NULL; +} + +long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) +{ + const struct cx18_api_info *info = find_api_info(mb->cmd); + struct cx18_mailbox *ack_mb; + u32 ack_irq; + u8 rpu = CPU; + + if (info == NULL && mb->cmd) { + CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); + return -EINVAL; + } + if (info) + rpu = info->rpu; + + switch (rpu) { + case HPU: + ack_irq = IRQ_EPU_TO_HPU_ACK; + ack_mb = &cx->scb->hpu2epu_mb; + break; + case APU: + ack_irq = IRQ_EPU_TO_APU_ACK; + ack_mb = &cx->scb->apu2epu_mb; + break; + case CPU: + ack_irq = IRQ_EPU_TO_CPU_ACK; + ack_mb = &cx->scb->cpu2epu_mb; + break; + default: + CX18_WARN("Unknown RPU for command %x\n", mb->cmd); + return -EINVAL; + } + + setup_page(SCB_OFFSET); + write_sync(mb->request, &ack_mb->ack); + write_reg(ack_irq, SW2_INT_SET); + return 0; +} + + +static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) +{ + const struct cx18_api_info *info = find_api_info(cmd); + u32 state = 0, irq = 0, req, oldreq, err; + struct cx18_mailbox *mb; + wait_queue_head_t *waitq; + int timeout = 100; + int cnt = 0; + int sig = 0; + int i; + + if (info == NULL) { + CX18_WARN("unknown cmd %x\n", cmd); + return -EINVAL; + } + + if (cmd == CX18_CPU_DE_SET_MDL) + CX18_DEBUG_HI_API("%s\n", info->name); + else + CX18_DEBUG_API("%s\n", info->name); + setup_page(SCB_OFFSET); + mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); + + if (mb == NULL) { + CX18_ERR("mb %s busy\n", info->name); + return -EBUSY; + } + + oldreq = req - 1; + writel(cmd, &mb->cmd); + for (i = 0; i < args; i++) + writel(data[i], &mb->args[i]); + writel(0, &mb->error); + writel(req, &mb->request); + + switch (info->rpu) { + case APU: waitq = &cx->mb_apu_waitq; break; + case CPU: waitq = &cx->mb_cpu_waitq; break; + case EPU: waitq = &cx->mb_epu_waitq; break; + case HPU: waitq = &cx->mb_hpu_waitq; break; + default: return -EINVAL; + } + if (info->flags & API_FAST) + timeout /= 2; + write_reg(irq, SW1_INT_SET); + + while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) { + if (cnt > 200 && !in_atomic()) + sig = cx18_msleep_timeout(10, 1); + cnt++; + } + if (sig) + return -EINTR; + if (cnt == 660) { + writel(oldreq, &mb->request); + CX18_ERR("mb %s failed\n", info->name); + return -EINVAL; + } + for (i = 0; i < MAX_MB_ARGUMENTS; i++) + data[i] = readl(&mb->args[i]); + err = readl(&mb->error); + if (!in_atomic() && (info->flags & API_SLOW)) + cx18_msleep_timeout(300, 0); + if (err) + CX18_DEBUG_API("mailbox error %08x for command %s\n", err, + info->name); + return err ? -EIO : 0; +} + +int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) +{ + int res = cx18_api_call(cx, cmd, args, data); + + /* Allow a single retry, probably already too late though. + If there is no free mailbox then that is usually an indication + of a more serious problem. */ + return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; +} + +static int cx18_set_filter_param(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + u32 mode; + int ret; + + mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0); + ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, + s->handle, 1, mode, cx->spatial_strength); + mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0); + ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, + s->handle, 0, mode, cx->temporal_strength); + ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, + s->handle, 2, cx->filter_mode >> 2, 0); + return ret; +} + +int cx18_api_func(void *priv, u32 cmd, int in, int out, + u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct cx18 *cx = priv; + struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; + + switch (cmd) { + case CX2341X_ENC_SET_OUTPUT_PORT: + return 0; + case CX2341X_ENC_SET_FRAME_RATE: + return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, + s->handle, 0, 0, 0, 0, data[0]); + case CX2341X_ENC_SET_FRAME_SIZE: + return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, + s->handle, data[1], data[0]); + case CX2341X_ENC_SET_STREAM_TYPE: + return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, + s->handle, data[0]); + case CX2341X_ENC_SET_ASPECT_RATIO: + return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, + s->handle, data[0]); + + case CX2341X_ENC_SET_GOP_PROPERTIES: + return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, + s->handle, data[0], data[1]); + case CX2341X_ENC_SET_GOP_CLOSURE: + return 0; + case CX2341X_ENC_SET_AUDIO_PROPERTIES: + return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, + s->handle, data[0]); + case CX2341X_ENC_MUTE_AUDIO: + return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, + s->handle, data[0]); + case CX2341X_ENC_SET_BIT_RATE: + return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, + s->handle, data[0], data[1], data[2], data[3]); + case CX2341X_ENC_MUTE_VIDEO: + return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, + s->handle, data[0]); + case CX2341X_ENC_SET_FRAME_DROP_RATE: + return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, + s->handle, data[0]); + case CX2341X_ENC_MISC: + return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, + s->handle, data[0], data[1], data[2]); + case CX2341X_ENC_SET_DNR_FILTER_MODE: + cx->filter_mode = (data[0] & 3) | (data[1] << 2); + return cx18_set_filter_param(s); + case CX2341X_ENC_SET_DNR_FILTER_PROPS: + cx->spatial_strength = data[0]; + cx->temporal_strength = data[1]; + return cx18_set_filter_param(s); + case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: + return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, + s->handle, data[0], data[1]); + case CX2341X_ENC_SET_CORING_LEVELS: + return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, + s->handle, data[0], data[1], data[2], data[3]); + } + CX18_WARN("Unknown cmd %x\n", cmd); + return 0; +} + +int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], + u32 cmd, int args, ...) +{ + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) + data[i] = va_arg(ap, u32); + va_end(ap); + return cx18_api(cx, cmd, args, data); +} + +int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...) +{ + u32 data[MAX_MB_ARGUMENTS]; + va_list ap; + int i; + + if (cx == NULL) { + CX18_ERR("cx == NULL (cmd=%x)\n", cmd); + return 0; + } + if (args > MAX_MB_ARGUMENTS) { + CX18_ERR("args too big (cmd=%x)\n", cmd); + args = MAX_MB_ARGUMENTS; + } + va_start(ap, args); + for (i = 0; i < args; i++) + data[i] = va_arg(ap, u32); + va_end(ap); + return cx18_api(cx, cmd, args, data); +} diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h new file mode 100644 index 000000000..d99564153 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-mailbox.h @@ -0,0 +1,73 @@ +/* + * cx18 mailbox functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef _CX18_MAILBOX_H_ +#define _CX18_MAILBOX_H_ + +/* mailbox max args */ +#define MAX_MB_ARGUMENTS 6 +/* compatibility, should be same as the define in cx2341x.h */ +#define CX2341X_MBOX_MAX_DATA 16 + +#define MB_RESERVED_HANDLE_0 0 +#define MB_RESERVED_HANDLE_1 0xFFFFFFFF + +struct cx18; + +/* The cx18_mailbox struct is the mailbox structure which is used for passing + messages between processors */ +struct cx18_mailbox { + /* The sender sets a handle in 'request' after he fills the command. The + 'request' should be different than 'ack'. The sender, also, generates + an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the + receiver. */ + u32 request; + /* The receiver detects a new command when 'req' is different than 'ack'. + He sets 'ack' to the same value as 'req' to clear the command. He, also, + generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU + is the receiver. */ + u32 ack; + u32 reserved[6]; + /* 'cmd' identifies the command. The list of these commands are in + cx23418.h */ + u32 cmd; + /* Each command can have up to 6 arguments */ + u32 args[MAX_MB_ARGUMENTS]; + /* The return code can be one of the codes in the file cx23418.h. If the + command is completed successfuly, the error will be ERR_SYS_SUCCESS. + If it is pending, the code is ERR_SYS_PENDING. If it failed, the error + code would indicate the task from which the error originated and will + be one of the errors in cx23418.h. In that case, the following + applies ((error & 0xff) != 0). + If the command is pending, the return will be passed in a MB from the + receiver to the sender. 'req' will be returned in args[0] */ + u32 error; +}; + +int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]); +int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, + int args, ...); +int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); +int cx18_api_func(void *priv, u32 cmd, int in, int out, + u32 data[CX2341X_MBOX_MAX_DATA]); +long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb); + +#endif diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c new file mode 100644 index 000000000..6990b77c6 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-queue.c @@ -0,0 +1,272 @@ +/* + * cx18 buffer queues + * + * Derived from ivtv-queue.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-streams.h" +#include "cx18-queue.h" +#include "cx18-scb.h" + +void cx18_buf_swap(struct cx18_buffer *buf) +{ + int i; + + for (i = 0; i < buf->bytesused; i += 4) + swab32s((u32 *)(buf->buf + i)); +} + +void cx18_queue_init(struct cx18_queue *q) +{ + INIT_LIST_HEAD(&q->list); + q->buffers = 0; + q->length = 0; + q->bytesused = 0; +} + +void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q) +{ + unsigned long flags = 0; + + /* clear the buffer if it is going to be enqueued to the free queue */ + if (q == &s->q_free) { + buf->bytesused = 0; + buf->readpos = 0; + buf->b_flags = 0; + } + spin_lock_irqsave(&s->qlock, flags); + list_add_tail(&buf->list, &q->list); + q->buffers++; + q->length += s->buf_size; + q->bytesused += buf->bytesused - buf->readpos; + spin_unlock_irqrestore(&s->qlock, flags); +} + +struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) +{ + struct cx18_buffer *buf = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&s->qlock, flags); + if (!list_empty(&q->list)) { + buf = list_entry(q->list.next, struct cx18_buffer, list); + list_del_init(q->list.next); + q->buffers--; + q->length -= s->buf_size; + q->bytesused -= buf->bytesused - buf->readpos; + } + spin_unlock_irqrestore(&s->qlock, flags); + return buf; +} + +struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, + u32 bytesused) +{ + struct cx18 *cx = s->cx; + struct list_head *p; + + list_for_each(p, &s->q_free.list) { + struct cx18_buffer *buf = + list_entry(p, struct cx18_buffer, list); + + if (buf->id != id) + continue; + buf->bytesused = bytesused; + /* the transport buffers are handled differently, + so there is no need to move them to the full queue */ + if (s->type == CX18_ENC_STREAM_TYPE_TS) + return buf; + s->q_free.buffers--; + s->q_free.length -= s->buf_size; + s->q_full.buffers++; + s->q_full.length += s->buf_size; + s->q_full.bytesused += buf->bytesused; + list_move_tail(&buf->list, &s->q_full.list); + return buf; + } + CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); + return NULL; +} + +static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from, + struct cx18_queue *to, int clear, int full) +{ + struct cx18_buffer *buf = + list_entry(from->list.next, struct cx18_buffer, list); + + list_move_tail(from->list.next, &to->list); + from->buffers--; + from->length -= s->buf_size; + from->bytesused -= buf->bytesused - buf->readpos; + /* special handling for q_free */ + if (clear) + buf->bytesused = buf->readpos = buf->b_flags = 0; + else if (full) { + /* special handling for stolen buffers, assume + all bytes are used. */ + buf->bytesused = s->buf_size; + buf->readpos = buf->b_flags = 0; + } + to->buffers++; + to->length += s->buf_size; + to->bytesused += buf->bytesused - buf->readpos; +} + +/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. + If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. + If 'steal' != NULL, then buffers may also taken from that queue if + needed. + + The buffer is automatically cleared if it goes to the free queue. It is + also cleared if buffers need to be taken from the 'steal' queue and + the 'from' queue is the free queue. + + When 'from' is q_free, then needed_bytes is compared to the total + available buffer length, otherwise needed_bytes is compared to the + bytesused value. For the 'steal' queue the total available buffer + length is always used. + + -ENOMEM is returned if the buffers could not be obtained, 0 if all + buffers where obtained from the 'from' list and if non-zero then + the number of stolen buffers is returned. */ +static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from, + struct cx18_queue *steal, struct cx18_queue *to, + int needed_bytes) +{ + unsigned long flags; + int rc = 0; + int from_free = from == &s->q_free; + int to_free = to == &s->q_free; + int bytes_available; + + spin_lock_irqsave(&s->qlock, flags); + if (needed_bytes == 0) { + from_free = 1; + needed_bytes = from->length; + } + + bytes_available = from_free ? from->length : from->bytesused; + bytes_available += steal ? steal->length : 0; + + if (bytes_available < needed_bytes) { + spin_unlock_irqrestore(&s->qlock, flags); + return -ENOMEM; + } + if (from_free) { + u32 old_length = to->length; + + while (to->length - old_length < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + cx18_queue_move_buf(s, from, to, 1, 0); + } + } else { + u32 old_bytesused = to->bytesused; + + while (to->bytesused - old_bytesused < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + cx18_queue_move_buf(s, from, to, to_free, rc); + } + } + spin_unlock_irqrestore(&s->qlock, flags); + return rc; +} + +void cx18_flush_queues(struct cx18_stream *s) +{ + cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0); + cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0); +} + +int cx18_stream_alloc(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + int i; + + if (s->buffers == 0) + return 0; + + CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n", + s->name, s->buffers, s->buf_size, + s->buffers * s->buf_size / 1024); + + if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - + (char *)cx->scb) > SCB_RESERVED_SIZE) { + unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE - + ((char *)cx->scb->cpu_mdl)); + + CX18_ERR("Too many buffers, cannot fit in SCB area\n"); + CX18_ERR("Max buffers = %zd\n", + bufsz / sizeof(struct cx18_mdl)); + return -ENOMEM; + } + + s->mdl_offset = cx->mdl_offset; + + /* allocate stream buffers. Initially all buffers are in q_free. */ + for (i = 0; i < s->buffers; i++) { + struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), + GFP_KERNEL|__GFP_NOWARN); + + if (buf == NULL) + break; + buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); + if (buf->buf == NULL) { + kfree(buf); + break; + } + buf->id = cx->buffer_id++; + INIT_LIST_HEAD(&buf->list); + buf->dma_handle = pci_map_single(s->cx->dev, + buf->buf, s->buf_size, s->dma); + cx18_buf_sync_for_cpu(s, buf); + cx18_enqueue(s, buf, &s->q_free); + } + if (i == s->buffers) { + cx->mdl_offset += s->buffers; + return 0; + } + CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); + cx18_stream_free(s); + return -ENOMEM; +} + +void cx18_stream_free(struct cx18_stream *s) +{ + struct cx18_buffer *buf; + + /* move all buffers to q_free */ + cx18_flush_queues(s); + + /* empty q_free */ + while ((buf = cx18_dequeue(s, &s->q_free))) { + pci_unmap_single(s->cx->dev, buf->dma_handle, + s->buf_size, s->dma); + kfree(buf->buf); + kfree(buf); + } +} diff --git a/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h new file mode 100644 index 000000000..91423b986 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-queue.h @@ -0,0 +1,55 @@ +/* + * cx18 buffer queues + * + * Derived from ivtv-queue.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#define CX18_DMA_UNMAPPED ((u32) -1) + +/* cx18_buffer utility functions */ + +static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, + struct cx18_buffer *buf) +{ + pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle, + s->buf_size, s->dma); +} + +static inline void cx18_buf_sync_for_device(struct cx18_stream *s, + struct cx18_buffer *buf) +{ + pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle, + s->buf_size, s->dma); +} + +void cx18_buf_swap(struct cx18_buffer *buf); + +/* cx18_queue utility functions */ +void cx18_queue_init(struct cx18_queue *q); +void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, + struct cx18_queue *q); +struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); +struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, + u32 bytesused); +void cx18_flush_queues(struct cx18_stream *s); + +/* cx18_stream utility functions */ +int cx18_stream_alloc(struct cx18_stream *s); +void cx18_stream_free(struct cx18_stream *s); diff --git a/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c new file mode 100644 index 000000000..30bc803e3 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-scb.c @@ -0,0 +1,121 @@ +/* + * cx18 System Control Block initialization + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-scb.h" + +void cx18_init_scb(struct cx18 *cx) +{ + setup_page(SCB_OFFSET); + memset_io(cx->scb, 0, 0x10000); + + writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); + writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); + writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); + writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); + writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); + writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); + writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); + writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); + + writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); + writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); + writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); + writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); + writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); + writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); + writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); + writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); + + writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); + writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); + writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); + writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); + writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); + writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); + writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); + writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); + + writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); + writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); + writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); + writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); + writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); + writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); + writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); + writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); + + writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); + writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); + writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); + writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); + writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); + writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); + writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); + writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); + + writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), + &cx->scb->apu2cpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), + &cx->scb->hpu2cpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), + &cx->scb->ppu2cpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), + &cx->scb->epu2cpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), + &cx->scb->cpu2apu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), + &cx->scb->hpu2apu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), + &cx->scb->ppu2apu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), + &cx->scb->epu2apu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), + &cx->scb->cpu2hpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), + &cx->scb->apu2hpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), + &cx->scb->ppu2hpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), + &cx->scb->epu2hpu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), + &cx->scb->cpu2ppu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), + &cx->scb->apu2ppu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), + &cx->scb->hpu2ppu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), + &cx->scb->epu2ppu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), + &cx->scb->cpu2epu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), + &cx->scb->apu2epu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), + &cx->scb->hpu2epu_mb_offset); + writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), + &cx->scb->ppu2epu_mb_offset); + + writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), + &cx->scb->ipc_offset); + + writel(1, &cx->scb->hpu_state); + writel(1, &cx->scb->epu_state); +} diff --git a/linux/drivers/media/video/cx18/cx18-scb.h b/linux/drivers/media/video/cx18/cx18-scb.h new file mode 100644 index 000000000..86b4cb15d --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-scb.h @@ -0,0 +1,285 @@ +/* + * cx18 System Control Block initialization + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef CX18_SCB_H +#define CX18_SCB_H + +#include "cx18-mailbox.h" + +/* NOTE: All ACK interrupts are in the SW2 register. All non-ACK interrupts + are in the SW1 register. */ + +#define IRQ_APU_TO_CPU 0x00000001 +#define IRQ_CPU_TO_APU_ACK 0x00000001 +#define IRQ_HPU_TO_CPU 0x00000002 +#define IRQ_CPU_TO_HPU_ACK 0x00000002 +#define IRQ_PPU_TO_CPU 0x00000004 +#define IRQ_CPU_TO_PPU_ACK 0x00000004 +#define IRQ_EPU_TO_CPU 0x00000008 +#define IRQ_CPU_TO_EPU_ACK 0x00000008 + +#define IRQ_CPU_TO_APU 0x00000010 +#define IRQ_APU_TO_CPU_ACK 0x00000010 +#define IRQ_HPU_TO_APU 0x00000020 +#define IRQ_APU_TO_HPU_ACK 0x00000020 +#define IRQ_PPU_TO_APU 0x00000040 +#define IRQ_APU_TO_PPU_ACK 0x00000040 +#define IRQ_EPU_TO_APU 0x00000080 +#define IRQ_APU_TO_EPU_ACK 0x00000080 + +#define IRQ_CPU_TO_HPU 0x00000100 +#define IRQ_HPU_TO_CPU_ACK 0x00000100 +#define IRQ_APU_TO_HPU 0x00000200 +#define IRQ_HPU_TO_APU_ACK 0x00000200 +#define IRQ_PPU_TO_HPU 0x00000400 +#define IRQ_HPU_TO_PPU_ACK 0x00000400 +#define IRQ_EPU_TO_HPU 0x00000800 +#define IRQ_HPU_TO_EPU_ACK 0x00000800 + +#define IRQ_CPU_TO_PPU 0x00001000 +#define IRQ_PPU_TO_CPU_ACK 0x00001000 +#define IRQ_APU_TO_PPU 0x00002000 +#define IRQ_PPU_TO_APU_ACK 0x00002000 +#define IRQ_HPU_TO_PPU 0x00004000 +#define IRQ_PPU_TO_HPU_ACK 0x00004000 +#define IRQ_EPU_TO_PPU 0x00008000 +#define IRQ_PPU_TO_EPU_ACK 0x00008000 + +#define IRQ_CPU_TO_EPU 0x00010000 +#define IRQ_EPU_TO_CPU_ACK 0x00010000 +#define IRQ_APU_TO_EPU 0x00020000 +#define IRQ_EPU_TO_APU_ACK 0x00020000 +#define IRQ_HPU_TO_EPU 0x00040000 +#define IRQ_EPU_TO_HPU_ACK 0x00040000 +#define IRQ_PPU_TO_EPU 0x00080000 +#define IRQ_EPU_TO_PPU_ACK 0x00080000 + +#define SCB_OFFSET 0xDC0000 + +/* If Firmware uses fixed memory map, it shall not allocate the area + between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */ +#define SCB_RESERVED_SIZE 0x10000 + + +/* This structure is used by EPU to provide memory descriptors in its memory */ +struct cx18_mdl { + u32 paddr; /* Physical address of a buffer segment */ + u32 length; /* Length of the buffer segment */ +}; + +/* This structure is used by CPU to provide completed buffers information */ +struct cx18_mdl_ack { + u32 id; /* ID of a completed MDL */ + u32 data_used; /* Total data filled in the MDL for buffer 'id' */ +}; + +struct cx18_scb { + /* These fields form the System Control Block which is used at boot time + for localizing the IPC data as well as the code positions for all + processors. The offsets are from the start of this struct. */ + + /* Offset where to find the Inter-Processor Communication data */ + u32 ipc_offset; + u32 reserved01[7]; + /* Offset where to find the start of the CPU code */ + u32 cpu_code_offset; + u32 reserved02[3]; + /* Offset where to find the start of the APU code */ + u32 apu_code_offset; + u32 reserved03[3]; + /* Offset where to find the start of the HPU code */ + u32 hpu_code_offset; + u32 reserved04[3]; + /* Offset where to find the start of the PPU code */ + u32 ppu_code_offset; + u32 reserved05[3]; + + /* These fields form Inter-Processor Communication data which is used + by all processors to locate the information needed for communicating + with other processors */ + + /* Fields for CPU: */ + + /* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */ + u32 cpu_state; + u32 reserved1[7]; + /* Offset to the mailbox used for sending commands from APU to CPU */ + u32 apu2cpu_mb_offset; + /* Value to write to register SW1 register set (0xC7003100) after the + command is ready */ + u32 apu2cpu_irq; + /* Value to write to register SW2 register set (0xC7003140) after the + command is cleared */ + u32 apu2cpu_irq_ack; + u32 reserved2[13]; + + u32 hpu2cpu_mb_offset; + u32 hpu2cpu_irq; + u32 hpu2cpu_irq_ack; + u32 reserved3[13]; + + u32 ppu2cpu_mb_offset; + u32 ppu2cpu_irq; + u32 ppu2cpu_irq_ack; + u32 reserved4[13]; + + u32 epu2cpu_mb_offset; + u32 epu2cpu_irq; + u32 epu2cpu_irq_ack; + u32 reserved5[13]; + u32 reserved6[8]; + + /* Fields for APU: */ + + u32 apu_state; + u32 reserved11[7]; + u32 cpu2apu_mb_offset; + u32 cpu2apu_irq; + u32 cpu2apu_irq_ack; + u32 reserved12[13]; + + u32 hpu2apu_mb_offset; + u32 hpu2apu_irq; + u32 hpu2apu_irq_ack; + u32 reserved13[13]; + + u32 ppu2apu_mb_offset; + u32 ppu2apu_irq; + u32 ppu2apu_irq_ack; + u32 reserved14[13]; + + u32 epu2apu_mb_offset; + u32 epu2apu_irq; + u32 epu2apu_irq_ack; + u32 reserved15[13]; + u32 reserved16[8]; + + /* Fields for HPU: */ + + u32 hpu_state; + u32 reserved21[7]; + u32 cpu2hpu_mb_offset; + u32 cpu2hpu_irq; + u32 cpu2hpu_irq_ack; + u32 reserved22[13]; + + u32 apu2hpu_mb_offset; + u32 apu2hpu_irq; + u32 apu2hpu_irq_ack; + u32 reserved23[13]; + + u32 ppu2hpu_mb_offset; + u32 ppu2hpu_irq; + u32 ppu2hpu_irq_ack; + u32 reserved24[13]; + + u32 epu2hpu_mb_offset; + u32 epu2hpu_irq; + u32 epu2hpu_irq_ack; + u32 reserved25[13]; + u32 reserved26[8]; + + /* Fields for PPU: */ + + u32 ppu_state; + u32 reserved31[7]; + u32 cpu2ppu_mb_offset; + u32 cpu2ppu_irq; + u32 cpu2ppu_irq_ack; + u32 reserved32[13]; + + u32 apu2ppu_mb_offset; + u32 apu2ppu_irq; + u32 apu2ppu_irq_ack; + u32 reserved33[13]; + + u32 hpu2ppu_mb_offset; + u32 hpu2ppu_irq; + u32 hpu2ppu_irq_ack; + u32 reserved34[13]; + + u32 epu2ppu_mb_offset; + u32 epu2ppu_irq; + u32 epu2ppu_irq_ack; + u32 reserved35[13]; + u32 reserved36[8]; + + /* Fields for EPU: */ + + u32 epu_state; + u32 reserved41[7]; + u32 cpu2epu_mb_offset; + u32 cpu2epu_irq; + u32 cpu2epu_irq_ack; + u32 reserved42[13]; + + u32 apu2epu_mb_offset; + u32 apu2epu_irq; + u32 apu2epu_irq_ack; + u32 reserved43[13]; + + u32 hpu2epu_mb_offset; + u32 hpu2epu_irq; + u32 hpu2epu_irq_ack; + u32 reserved44[13]; + + u32 ppu2epu_mb_offset; + u32 ppu2epu_irq; + u32 ppu2epu_irq_ack; + u32 reserved45[13]; + u32 reserved46[8]; + + u32 semaphores[8]; /* Semaphores */ + + u32 reserved50[32]; /* Reserved for future use */ + + struct cx18_mailbox apu2cpu_mb; + struct cx18_mailbox hpu2cpu_mb; + struct cx18_mailbox ppu2cpu_mb; + struct cx18_mailbox epu2cpu_mb; + + struct cx18_mailbox cpu2apu_mb; + struct cx18_mailbox hpu2apu_mb; + struct cx18_mailbox ppu2apu_mb; + struct cx18_mailbox epu2apu_mb; + + struct cx18_mailbox cpu2hpu_mb; + struct cx18_mailbox apu2hpu_mb; + struct cx18_mailbox ppu2hpu_mb; + struct cx18_mailbox epu2hpu_mb; + + struct cx18_mailbox cpu2ppu_mb; + struct cx18_mailbox apu2ppu_mb; + struct cx18_mailbox hpu2ppu_mb; + struct cx18_mailbox epu2ppu_mb; + + struct cx18_mailbox cpu2epu_mb; + struct cx18_mailbox apu2epu_mb; + struct cx18_mailbox hpu2epu_mb; + struct cx18_mailbox ppu2epu_mb; + + struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2]; + struct cx18_mdl cpu_mdl[1]; +}; + +void cx18_init_scb(struct cx18 *cx); + +#endif diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c new file mode 100644 index 000000000..352ff698a --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -0,0 +1,603 @@ +/* + * cx18 init/start/stop/exit stream functions + * + * Derived from ivtv-streams.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-fileops.h" +#include "cx18-mailbox.h" +#include "cx18-i2c.h" +#include "cx18-queue.h" +#include "cx18-ioctl.h" +#include "cx18-streams.h" +#include "cx18-cards.h" +#include "cx18-scb.h" +#include "cx18-av-core.h" +#include "cx18-dvb.h" + +#define CX18_DSP0_INTERRUPT_MASK 0xd0004C + +static struct file_operations cx18_v4l2_enc_fops = { + .owner = THIS_MODULE, + .read = cx18_v4l2_read, + .open = cx18_v4l2_open, + .ioctl = cx18_v4l2_ioctl, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .compat_ioctl = v4l_compat_ioctl32, +#endif + .release = cx18_v4l2_close, + .poll = cx18_v4l2_enc_poll, +}; + +/* offset from 0 to register ts v4l2 minors on */ +#define CX18_V4L2_ENC_TS_OFFSET 16 +/* offset from 0 to register pcm v4l2 minors on */ +#define CX18_V4L2_ENC_PCM_OFFSET 24 +/* offset from 0 to register yuv v4l2 minors on */ +#define CX18_V4L2_ENC_YUV_OFFSET 32 + +static struct { + const char *name; + int vfl_type; + int minor_offset; + int dma; + enum v4l2_buf_type buf_type; + struct file_operations *fops; +} cx18_stream_info[] = { + { /* CX18_ENC_STREAM_TYPE_MPG */ + "encoder MPEG", + VFL_TYPE_GRABBER, 0, + PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &cx18_v4l2_enc_fops + }, + { /* CX18_ENC_STREAM_TYPE_TS */ + "TS", + VFL_TYPE_GRABBER, -1, + PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &cx18_v4l2_enc_fops + }, + { /* CX18_ENC_STREAM_TYPE_YUV */ + "encoder YUV", + VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, + PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &cx18_v4l2_enc_fops + }, + { /* CX18_ENC_STREAM_TYPE_VBI */ + "encoder VBI", + VFL_TYPE_VBI, 0, + PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE, + &cx18_v4l2_enc_fops + }, + { /* CX18_ENC_STREAM_TYPE_PCM */ + "encoder PCM audio", + VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, + PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE, + &cx18_v4l2_enc_fops + }, + { /* CX18_ENC_STREAM_TYPE_IDX */ + "encoder IDX", + VFL_TYPE_GRABBER, -1, + PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &cx18_v4l2_enc_fops + }, + { /* CX18_ENC_STREAM_TYPE_RAD */ + "encoder radio", + VFL_TYPE_RADIO, 0, + PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE, + &cx18_v4l2_enc_fops + }, +}; + +static void cx18_stream_init(struct cx18 *cx, int type) +{ + struct cx18_stream *s = &cx->streams[type]; + struct video_device *dev = s->v4l2dev; + u32 max_size = cx->options.megabytes[type] * 1024 * 1024; + + /* we need to keep v4l2dev, so restore it afterwards */ + memset(s, 0, sizeof(*s)); + s->v4l2dev = dev; + + /* initialize cx18_stream fields */ + s->cx = cx; + s->type = type; + s->name = cx18_stream_info[type].name; + s->handle = 0xffffffff; + + s->dma = cx18_stream_info[type].dma; + s->buf_size = cx->stream_buf_size[type]; + if (s->buf_size) + s->buffers = max_size / s->buf_size; + if (s->buffers > 63) { + /* Each stream has a maximum of 63 buffers, + ensure we do not exceed that. */ + s->buffers = 63; + s->buf_size = (max_size / s->buffers) & ~0xfff; + } + spin_lock_init(&s->qlock); + init_waitqueue_head(&s->waitq); + s->id = -1; + cx18_queue_init(&s->q_free); + cx18_queue_init(&s->q_full); + cx18_queue_init(&s->q_io); +} + +static int cx18_prep_dev(struct cx18 *cx, int type) +{ + struct cx18_stream *s = &cx->streams[type]; + u32 cap = cx->v4l2_cap; + int minor_offset = cx18_stream_info[type].minor_offset; + int minor; + + /* These four fields are always initialized. If v4l2dev == NULL, then + this stream is not in use. In that case no other fields but these + four can be used. */ + s->v4l2dev = NULL; + s->cx = cx; + s->type = type; + s->name = cx18_stream_info[type].name; + + /* Check whether the radio is supported */ + if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO)) + return 0; + + /* Check whether VBI is supported */ + if (type == CX18_ENC_STREAM_TYPE_VBI && + !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) + return 0; + + /* card number + user defined offset + device offset */ + minor = cx->num + cx18_first_minor + minor_offset; + + /* User explicitly selected 0 buffers for these streams, so don't + create them. */ + if (cx18_stream_info[type].dma != PCI_DMA_NONE && + cx->options.megabytes[type] == 0) { + CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); + return 0; + } + + cx18_stream_init(cx, type); + + if (minor_offset == -1) + return 0; + + /* allocate and initialize the v4l2 video device structure */ + s->v4l2dev = video_device_alloc(); + if (s->v4l2dev == NULL) { + CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", + s->name); + return -ENOMEM; + } + + s->v4l2dev->type = + VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | + VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s", + cx->num, s->name); + + s->v4l2dev->minor = minor; + s->v4l2dev->dev = &cx->dev->dev; + s->v4l2dev->fops = cx18_stream_info[type].fops; + s->v4l2dev->release = video_device_release; + + return 0; +} + +/* Initialize v4l2 variables and register v4l2 devices */ +int cx18_streams_setup(struct cx18 *cx) +{ + int type; + + /* Setup V4L2 Devices */ + for (type = 0; type < CX18_MAX_STREAMS; type++) { + /* Prepare device */ + if (cx18_prep_dev(cx, type)) + break; + + /* Allocate Stream */ + if (cx18_stream_alloc(&cx->streams[type])) + break; + } + if (type == CX18_MAX_STREAMS) + return 0; + + /* One or more streams could not be initialized. Clean 'em all up. */ + cx18_streams_cleanup(cx, 0); + return -ENOMEM; +} + +static int cx18_reg_dev(struct cx18 *cx, int type) +{ + struct cx18_stream *s = &cx->streams[type]; + int vfl_type = cx18_stream_info[type].vfl_type; + int minor; + + /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something? + * We need a VFL_TYPE_TS defined. + */ + if (strcmp("TS", s->name) == 0) { + /* just return if no DVB is supported */ + if ((cx->card->hw_all & CX18_HW_DVB) == 0) + return 0; + if (cx18_dvb_register(s) < 0) { + CX18_ERR("DVB failed to register\n"); + return -EINVAL; + } + } + + if (s->v4l2dev == NULL) + return 0; + + minor = s->v4l2dev->minor; + + /* Register device. First try the desired minor, then any free one. */ + if (video_register_device(s->v4l2dev, vfl_type, minor) && + video_register_device(s->v4l2dev, vfl_type, -1)) { + CX18_ERR("Couldn't register v4l2 device for %s minor %d\n", + s->name, minor); + video_device_release(s->v4l2dev); + s->v4l2dev = NULL; + return -ENOMEM; + } + minor = s->v4l2dev->minor; + + switch (vfl_type) { + case VFL_TYPE_GRABBER: + CX18_INFO("Registered device video%d for %s (%d MB)\n", + minor, s->name, cx->options.megabytes[type]); + break; + + case VFL_TYPE_RADIO: + CX18_INFO("Registered device radio%d for %s\n", + minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); + break; + + case VFL_TYPE_VBI: + if (cx->options.megabytes[type]) + CX18_INFO("Registered device vbi%d for %s (%d MB)\n", + minor - MINOR_VFL_TYPE_VBI_MIN, + s->name, cx->options.megabytes[type]); + else + CX18_INFO("Registered device vbi%d for %s\n", + minor - MINOR_VFL_TYPE_VBI_MIN, s->name); + break; + } + + return 0; +} + +/* Register v4l2 devices */ +int cx18_streams_register(struct cx18 *cx) +{ + int type; + int err = 0; + + /* Register V4L2 devices */ + for (type = 0; type < CX18_MAX_STREAMS; type++) + err |= cx18_reg_dev(cx, type); + + if (err == 0) + return 0; + + /* One or more streams could not be initialized. Clean 'em all up. */ + cx18_streams_cleanup(cx, 1); + return -ENOMEM; +} + +/* Unregister v4l2 devices */ +void cx18_streams_cleanup(struct cx18 *cx, int unregister) +{ + struct video_device *vdev; + int type; + + /* Teardown all streams */ + for (type = 0; type < CX18_MAX_STREAMS; type++) { + if (cx->streams[type].dvb.enabled) + cx18_dvb_unregister(&cx->streams[type]); + + vdev = cx->streams[type].v4l2dev; + + cx->streams[type].v4l2dev = NULL; + if (vdev == NULL) + continue; + + cx18_stream_free(&cx->streams[type]); + + /* Unregister or release device */ + if (unregister) + video_unregister_device(vdev); + else + video_device_release(vdev); + } +} + +static void cx18_vbi_setup(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + int raw = cx->vbi.sliced_in->service_set == 0; + u32 data[CX2341X_MBOX_MAX_DATA]; + int lines; + + if (cx->is_60hz) { + cx->vbi.count = 12; + cx->vbi.start[0] = 10; + cx->vbi.start[1] = 273; + } else { /* PAL/SECAM */ + cx->vbi.count = 18; + cx->vbi.start[0] = 6; + cx->vbi.start[1] = 318; + } + + /* setup VBI registers */ + cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); + + /* determine number of lines and total number of VBI bytes. + A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 + The '- 1' byte is probably an unused U or V byte. Or something... + A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal + header, 42 data bytes + checksum (to be confirmed) */ + if (raw) { + lines = cx->vbi.count * 2; + } else { + lines = cx->is_60hz ? 24 : 38; + if (cx->is_60hz) + lines += 2; + } + + cx->vbi.enc_size = lines * + (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); + + data[0] = s->handle; + /* Lines per field */ + data[1] = (lines / 2) | ((lines / 2) << 16); + /* bytes per line */ + data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); + /* Every X number of frames a VBI interrupt arrives + (frames as in 25 or 30 fps) */ + data[3] = 1; + /* Setup VBI for the cx25840 digitizer */ + if (raw) { + data[4] = 0x20602060; + data[5] = 0x30703070; + } else { + data[4] = 0xB0F0B0F0; + data[5] = 0xA0E0A0E0; + } + + CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", + data[0], data[1], data[2], data[3], data[4], data[5]); + + if (s->type == CX18_ENC_STREAM_TYPE_VBI) + cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); +} + +int cx18_start_v4l2_encode_stream(struct cx18_stream *s) +{ + u32 data[MAX_MB_ARGUMENTS]; + struct cx18 *cx = s->cx; + struct list_head *p; + int ts = 0; + int captype = 0; + + if (s->v4l2dev == NULL && s->dvb.enabled == 0) + return -EINVAL; + + CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); + + switch (s->type) { + case CX18_ENC_STREAM_TYPE_MPG: + captype = CAPTURE_CHANNEL_TYPE_MPEG; + cx->mpg_data_received = cx->vbi_data_inserted = 0; + cx->dualwatch_jiffies = jiffies; + cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300; + cx->search_pack_header = 0; + break; + + case CX18_ENC_STREAM_TYPE_TS: + captype = CAPTURE_CHANNEL_TYPE_TS; + ts = 1; + break; + case CX18_ENC_STREAM_TYPE_YUV: + captype = CAPTURE_CHANNEL_TYPE_YUV; + break; + case CX18_ENC_STREAM_TYPE_PCM: + captype = CAPTURE_CHANNEL_TYPE_PCM; + break; + case CX18_ENC_STREAM_TYPE_VBI: + captype = cx->vbi.sliced_in->service_set ? + CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI; + cx->vbi.frame = 0; + cx->vbi.inserted_frame = 0; + memset(cx->vbi.sliced_mpeg_size, + 0, sizeof(cx->vbi.sliced_mpeg_size)); + break; + default: + return -EINVAL; + } + s->buffers_stolen = 0; + + /* mute/unmute video */ + cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, + s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)); + + /* Clear Streamoff flags in case left from last capture */ + clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); + + cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); + s->handle = data[0]; + cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); + + if (atomic_read(&cx->ana_capturing) == 0 && !ts) { + /* Stuff from Windows, we don't know what it is */ + cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); + + cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, + s->handle, cx->digitizer, cx->digitizer); + + /* Setup VBI */ + if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) + cx18_vbi_setup(s); + + /* assign program index info. + Mask 7: select I/P/B, Num_req: 400 max */ + cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); + + /* Setup API for Stream */ + cx2341x_update(cx, cx18_api_func, NULL, &cx->params); + } + + if (atomic_read(&cx->tot_capturing) == 0) { + clear_bit(CX18_F_I_EOS, &cx->i_flags); + write_reg(7, CX18_DSP0_INTERRUPT_MASK); + } + + cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, + (void *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, + (void *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); + + list_for_each(p, &s->q_free.list) { + struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); + + writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); + writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length); + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, + (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1, + buf->id, s->buf_size); + } + /* begin_capture */ + if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { + CX18_DEBUG_WARN("Error starting capture!\n"); + cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); + return -EINVAL; + } + + /* you're live! sit back and await interrupts :) */ + if (!ts) + atomic_inc(&cx->ana_capturing); + atomic_inc(&cx->tot_capturing); + return 0; +} + +void cx18_stop_all_captures(struct cx18 *cx) +{ + int i; + + for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { + struct cx18_stream *s = &cx->streams[i]; + + if (s->v4l2dev == NULL && s->dvb.enabled == 0) + continue; + if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) + cx18_stop_v4l2_encode_stream(s, 0); + } +} + +int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) +{ + struct cx18 *cx = s->cx; + unsigned long then; + + if (s->v4l2dev == NULL && s->dvb.enabled == 0) + return -EINVAL; + + /* This function assumes that you are allowed to stop the capture + and that we are actually capturing */ + + CX18_DEBUG_INFO("Stop Capture\n"); + + if (atomic_read(&cx->tot_capturing) == 0) + return 0; + + if (s->type == CX18_ENC_STREAM_TYPE_MPG) + cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); + else + cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); + + then = jiffies; + + if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { +#if 0 + /* only run these if we're shutting down the last cap */ + DECLARE_WAITQUEUE(wait, current); + unsigned long duration; + + then = jiffies; + add_wait_queue(&cx->cap_w, &wait); + + set_current_state(TASK_INTERRUPTIBLE); + + /* TODO: wait 2s for EOS interrupt */ + while (!test_bit(CX18_F_I_EOS, &cx->i_flags) && + time_before(jiffies, then + msecs_to_jiffies(2000))) + schedule_timeout(msecs_to_jiffies(10)); + + duration = jiffies_to_msecs(jiffies - then); + + if (!test_bit(CX18_F_I_EOS, &cx->i_flags)) { + CX18_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); + CX18_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); + } else { + CX18_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&cx->cap_w, &wait); +#else + CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); +#endif + } + + if (s->type != CX18_ENC_STREAM_TYPE_TS) + atomic_dec(&cx->ana_capturing); + atomic_dec(&cx->tot_capturing); + + /* Clear capture and no-read bits */ + clear_bit(CX18_F_S_STREAMING, &s->s_flags); + + cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); + s->handle = 0xffffffff; + + if (atomic_read(&cx->tot_capturing) > 0) + return 0; + + write_reg(5, CX18_DSP0_INTERRUPT_MASK); + wake_up(&s->waitq); + + return 0; +} + +u32 cx18_find_handle(struct cx18 *cx) +{ + int i; + + /* find first available handle to be used for global settings */ + for (i = 0; i < CX18_MAX_STREAMS; i++) { + struct cx18_stream *s = &cx->streams[i]; + + if (s->v4l2dev && s->handle) + return s->handle; + } + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h new file mode 100644 index 000000000..f327e947b --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-streams.h @@ -0,0 +1,33 @@ +/* + * cx18 init/start/stop/exit stream functions + * + * Derived from ivtv-streams.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +u32 cx18_find_handle(struct cx18 *cx); +int cx18_streams_setup(struct cx18 *cx); +int cx18_streams_register(struct cx18 *cx); +void cx18_streams_cleanup(struct cx18 *cx, int unregister); + +/* Capture related */ +int cx18_start_v4l2_encode_stream(struct cx18_stream *s); +int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); + +void cx18_stop_all_captures(struct cx18 *cx); diff --git a/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c new file mode 100644 index 000000000..22e76ee3f --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-vbi.c @@ -0,0 +1,208 @@ +/* + * cx18 Vertical Blank Interval support functions + * + * Derived from ivtv-vbi.c + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-vbi.h" +#include "cx18-ioctl.h" +#include "cx18-queue.h" +#include "cx18-av-core.h" + +static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) +{ + int line = 0; + int i; + u32 linemask[2] = { 0, 0 }; + unsigned short size; + static const u8 mpeg_hdr_data[] = { + 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, + 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, + 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff + }; + const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ + int idx = cx->vbi.frame % CX18_VBI_FRAMES; + u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0]; + + for (i = 0; i < lines; i++) { + struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i; + int f, l; + + if (sdata->id == 0) + continue; + + l = sdata->line - 6; + f = sdata->field; + if (f) + l += 18; + if (l < 32) + linemask[0] |= (1 << l); + else + linemask[1] |= (1 << (l - 32)); + dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id); + memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42); + line++; + } + memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); + if (line == 36) { + /* All lines are used, so there is no space for the linemask + (the max size of the VBI data is 36 * 43 + 4 bytes). + So in this case we use the magic number 'ITV0'. */ + memcpy(dst + sd, "ITV0", 4); + memcpy(dst + sd + 4, dst + sd + 12, line * 43); + size = 4 + ((43 * line + 3) & ~3); + } else { + memcpy(dst + sd, "cx0", 4); + memcpy(dst + sd + 4, &linemask[0], 8); + size = 12 + ((43 * line + 3) & ~3); + } + dst[4+16] = (size + 10) >> 8; + dst[5+16] = (size + 10) & 0xff; + dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); + dst[10+16] = (pts_stamp >> 22) & 0xff; + dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); + dst[12+16] = (pts_stamp >> 7) & 0xff; + dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); + cx->vbi.sliced_mpeg_size[idx] = sd + size; +} + +/* Compress raw VBI format, removes leading SAV codes and surplus space + after the field. + Returns new compressed size. */ +static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size) +{ + u32 line_size = cx->vbi.raw_decoder_line_size; + u32 lines = cx->vbi.count; + u8 sav1 = cx->vbi.raw_decoder_sav_odd_field; + u8 sav2 = cx->vbi.raw_decoder_sav_even_field; + u8 *q = buf; + u8 *p; + int i; + + for (i = 0; i < lines; i++) { + p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || + (p[3] != sav1 && p[3] != sav2)) + break; + memcpy(q, p + 4, line_size - 4); + q += line_size - 4; + } + return lines * (line_size - 4); +} + + +/* Compressed VBI format, all found sliced blocks put next to one another + Returns new compressed size */ +static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, + u32 size, u8 sav) +{ + u32 line_size = cx->vbi.sliced_decoder_line_size; + struct v4l2_decode_vbi_line vbi; + int i; + + /* find the first valid line */ + for (i = 0; i < size; i++, buf++) { + if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) + break; + } + + size -= i; + if (size < line_size) + return line; + for (i = 0; i < size / line_size; i++) { + u8 *p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) + continue; + vbi.p = p + 4; + cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + if (vbi.type) { + cx->vbi.sliced_data[line].id = vbi.type; + cx->vbi.sliced_data[line].field = vbi.is_second_field; + cx->vbi.sliced_data[line].line = vbi.line; + memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42); + line++; + } + } + return line; +} + +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, + u64 pts_stamp, int streamtype) +{ + u8 *p = (u8 *) buf->buf; + u32 size = buf->bytesused; + int lines; + + if (streamtype != CX18_ENC_STREAM_TYPE_VBI) + return; + + /* Raw VBI data */ + if (cx->vbi.sliced_in->service_set == 0) { + u8 type; + + cx18_buf_swap(buf); + + type = p[3]; + + size = buf->bytesused = compress_raw_buf(cx, p, size); + + /* second field of the frame? */ + if (type == cx->vbi.raw_decoder_sav_even_field) { + /* Dirty hack needed for backwards + compatibility of old VBI software. */ + p += size - 4; + memcpy(p, &cx->vbi.frame, 4); + cx->vbi.frame++; + } + return; + } + + /* Sliced VBI data with data insertion */ + cx18_buf_swap(buf); + + /* first field */ + lines = compress_sliced_buf(cx, 0, p, size / 2, + cx->vbi.sliced_decoder_sav_odd_field); + /* second field */ + /* experimentation shows that the second half does not always + begin at the exact address. So start a bit earlier + (hence 32). */ + lines = compress_sliced_buf(cx, lines, p + size / 2 - 32, + size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field); + /* always return at least one empty line */ + if (lines == 0) { + cx->vbi.sliced_data[0].id = 0; + cx->vbi.sliced_data[0].line = 0; + cx->vbi.sliced_data[0].field = 0; + lines = 1; + } + buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]); + memcpy(p, &cx->vbi.sliced_data[0], size); + + if (cx->vbi.insert_mpeg) + copy_vbi_data(cx, lines, pts_stamp); + cx->vbi.frame++; +} diff --git a/linux/drivers/media/video/cx18/cx18-vbi.h b/linux/drivers/media/video/cx18/cx18-vbi.h new file mode 100644 index 000000000..c56ff7d28 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-vbi.h @@ -0,0 +1,26 @@ +/* + * cx18 Vertical Blank Interval support functions + * + * Derived from ivtv-vbi.h + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, + u64 pts_stamp, int streamtype); +int cx18_used_line(struct cx18 *cx, int line, int field); diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h new file mode 100644 index 000000000..d5c7a6f96 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-version.h @@ -0,0 +1,34 @@ +/* + * cx18 driver version information + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef CX18_VERSION_H +#define CX18_VERSION_H + +#define CX18_DRIVER_NAME "cx18" +#define CX18_DRIVER_VERSION_MAJOR 1 +#define CX18_DRIVER_VERSION_MINOR 0 +#define CX18_DRIVER_VERSION_PATCHLEVEL 0 + +#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) +#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \ + CX18_DRIVER_VERSION_MINOR, CX18_DRIVER_VERSION_PATCHLEVEL) + +#endif diff --git a/linux/drivers/media/video/cx18/cx18-video.c b/linux/drivers/media/video/cx18/cx18-video.c new file mode 100644 index 000000000..2e5c41939 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-video.c @@ -0,0 +1,45 @@ +/* + * cx18 video interface functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-video.h" +#include "cx18-av-core.h" +#include "cx18-cards.h" + +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; + cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_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 */ +} diff --git a/linux/drivers/media/video/cx18/cx18-video.h b/linux/drivers/media/video/cx18/cx18-video.h new file mode 100644 index 000000000..529006a06 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-video.h @@ -0,0 +1,22 @@ +/* + * cx18 video interface functions + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +void cx18_video_set_io(struct cx18 *cx); diff --git a/linux/drivers/media/video/cx18/cx23418.h b/linux/drivers/media/video/cx18/cx23418.h new file mode 100644 index 000000000..33f78da9d --- /dev/null +++ b/linux/drivers/media/video/cx18/cx23418.h @@ -0,0 +1,458 @@ +/* + * cx18 header containing common defines. + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef CX23418_H +#define CX23418_H + +#include <media/cx2341x.h> + +#define MGR_CMD_MASK 0x40000000 +/* The MSB of the command code indicates that this is the completion of a + command */ +#define MGR_CMD_MASK_ACK (MGR_CMD_MASK | 0x80000000) + +/* Description: This command creates a new instance of a certain task + IN[0] - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is + the processor on which the task YYY will be created + OUT[0] - Task handle. This handle is passed along with commands to + dispatch to the right instance of the task + ReturnCode - One of the ERR_SYS_... */ +#define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001) + +/* Description: This command destroys an instance of a task + IN[0] - Task handle. Hanlde of the task to destroy + ReturnCode - One of the ERR_SYS_... */ +#define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002) + +/* All commands for CPU have the following mask set */ +#define CPU_CMD_MASK 0x20000000 +#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000) +#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000) +#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000) + +#define EPU_CMD_MASK 0x02000000 +#define EPU_CMD_MASK_DEBUG (EPU_CMD_MASK | 0x000000) +#define EPU_CMD_MASK_DE (EPU_CMD_MASK | 0x040000) + +/* Description: This command indicates that a Memory Descriptor List has been + filled with the requested channel type + IN[0] - Task handle. Handle of the task + IN[1] - Offset of the MDL_ACK from the beginning of the local DDR. + IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1] + ReturnCode - One of the ERR_DE_... */ +#define CX18_EPU_DMA_DONE (EPU_CMD_MASK_DE | 0x0001) + +/* Something interesting happened + IN[0] - A value to log + IN[1] - An offset of a string in the MiniMe memory; + 0/zero/NULL means "I have nothing to say" */ +#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003) + +/* Description: This command starts streaming with the set channel type + IN[0] - Task handle. Handle of the task to start + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_CAPTURE_START (CPU_CMD_MASK_CAPTURE | 0x0002) + +/* Description: This command stops streaming with the set channel type + IN[0] - Task handle. Handle of the task to stop + IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only) + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_CAPTURE_STOP (CPU_CMD_MASK_CAPTURE | 0x0003) + +/* Description: This command pauses streaming with the set channel type + IN[0] - Task handle. Handle of the task to pause + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_CAPTURE_PAUSE (CPU_CMD_MASK_CAPTURE | 0x0007) + +/* Description: This command resumes streaming with the set channel type + IN[0] - Task handle. Handle of the task to resume + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_CAPTURE_RESUME (CPU_CMD_MASK_CAPTURE | 0x0008) + +#define CAPTURE_CHANNEL_TYPE_NONE 0 +#define CAPTURE_CHANNEL_TYPE_MPEG 1 +#define CAPTURE_CHANNEL_TYPE_INDEX 2 +#define CAPTURE_CHANNEL_TYPE_YUV 3 +#define CAPTURE_CHANNEL_TYPE_PCM 4 +#define CAPTURE_CHANNEL_TYPE_VBI 5 +#define CAPTURE_CHANNEL_TYPE_SLICED_VBI 6 +#define CAPTURE_CHANNEL_TYPE_TS 7 +#define CAPTURE_CHANNEL_TYPE_MAX 15 + +/* Description: This command sets the channel type. This can only be done + when stopped. + IN[0] - Task handle. Handle of the task to start + IN[1] - Channel Type. See Below. + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_CHANNEL_TYPE (CPU_CMD_MASK_CAPTURE + 1) + +/* Description: Set stream output type + IN[0] - task handle. Handle of the task to start + IN[1] - type + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_STREAM_OUTPUT_TYPE (CPU_CMD_MASK_CAPTURE | 0x0012) + +/* Description: Set video input resolution and frame rate + IN[0] - task handle + IN[1] - reserved + IN[2] - reserved + IN[3] - reserved + IN[4] - reserved + IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_VIDEO_IN (CPU_CMD_MASK_CAPTURE | 0x0004) + +/* Description: Set video frame rate + IN[0] - task handle. Handle of the task to start + IN[1] - video bit rate mode + IN[2] - video average rate + IN[3] - video peak rate + IN[4] - system mux rate + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_VIDEO_RATE (CPU_CMD_MASK_CAPTURE | 0x0005) + +/* Description: Set video output resolution + IN[0] - task handle + IN[1] - horizontal size + IN[2] - vertical size + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_VIDEO_RESOLUTION (CPU_CMD_MASK_CAPTURE | 0x0006) + +/* Description: This command set filter parameters + IN[0] - Task handle. Handle of the task + IN[1] - type, 0 - temporal, 1 - spatial, 2 - median + IN[2] - mode, temporal/spatial: 0 - disable, 1 - static, 2 - dynamic + median: 0 = disable, 1 = horizontal, 2 = vertical, + 3 = horizontal/vertical, 4 = diagonal + IN[3] - strength, temporal 0 - 31, spatial 0 - 15 + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_FILTER_PARAM (CPU_CMD_MASK_CAPTURE | 0x0009) + +/* Description: This command set spatial filter type + IN[0] - Task handle. + IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only, + 3 = 2D H/V separable, 4 = 2D symmetric non-separable + IN[2] - chroma type: 0 - diable, 1 = 1D horizontal + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_SPATIAL_FILTER_TYPE (CPU_CMD_MASK_CAPTURE | 0x000C) + +/* Description: This command set coring levels for median filter + IN[0] - Task handle. + IN[1] - luma_high + IN[2] - luma_low + IN[3] - chroma_high + IN[4] - chroma_low + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E) + +/* Description: This command set the picture type mask for index file + IN[0] - 0 = disable index file output + 1 = output I picture + 2 = P picture + 4 = B picture + other = illegal */ +#define CX18_CPU_SET_INDEXTABLE (CPU_CMD_MASK_CAPTURE | 0x0010) + +/* Description: Set audio parameters + IN[0] - task handle. Handle of the task to start + IN[1] - audio parameter + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_AUDIO_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0011) + +/* Description: Set video mute + IN[0] - task handle. Handle of the task to start + IN[1] - bit31-24: muteYvalue + bit23-16: muteUvalue + bit15-8: muteVvalue + bit0: 1:mute, 0: unmute + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_VIDEO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0013) + +/* Description: Set audio mute + IN[0] - task handle. Handle of the task to start + IN[1] - mute/unmute + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_AUDIO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0014) + +/* Description: Set stream output type + IN[0] - task handle. Handle of the task to start + IN[1] - subType + SET_INITIAL_SCR 1 + SET_QUALITY_MODE 2 + SET_VIM_PROTECT_MODE 3 + SET_PTS_CORRECTION 4 + SET_USB_FLUSH_MODE 5 + SET_MERAQPAR_ENABLE 6 + SET_NAV_PACK_INSERTION 7 + SET_SCENE_CHANGE_ENABLE 8 + IN[2] - parameter 1 + IN[3] - parameter 2 + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_MISC_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0015) + +/* Description: Set raw VBI parameters + IN[0] - Task handle + IN[1] - No. of input lines per field: + bit[15:0]: field 1, + bit[31:16]: field 2 + IN[2] - No. of input bytes per line + IN[3] - No. of output frames per transfer + IN[4] - start code + IN[5] - stop code + ReturnCode */ +#define CX18_CPU_SET_RAW_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0016) + +/* Description: Set capture line No. + IN[0] - task handle. Handle of the task to start + IN[1] - height1 + IN[2] - height2 + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_CAPTURE_LINE_NO (CPU_CMD_MASK_CAPTURE | 0x0017) + +/* Description: Set copyright + IN[0] - task handle. Handle of the task to start + IN[1] - copyright + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_COPYRIGHT (CPU_CMD_MASK_CAPTURE | 0x0018) + +/* Description: Set audio PID + IN[0] - task handle. Handle of the task to start + IN[1] - PID + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_AUDIO_PID (CPU_CMD_MASK_CAPTURE | 0x0019) + +/* Description: Set video PID + IN[0] - task handle. Handle of the task to start + IN[1] - PID + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_VIDEO_PID (CPU_CMD_MASK_CAPTURE | 0x001A) + +/* Description: Set Vertical Crop Line + IN[0] - task handle. Handle of the task to start + IN[1] - Line + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_VER_CROP_LINE (CPU_CMD_MASK_CAPTURE | 0x001B) + +/* Description: Set COP structure + IN[0] - task handle. Handle of the task to start + IN[1] - M + IN[2] - N + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_GOP_STRUCTURE (CPU_CMD_MASK_CAPTURE | 0x001C) + +/* Description: Set Scene Change Detection + IN[0] - task handle. Handle of the task to start + IN[1] - scene change + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_SCENE_CHANGE_DETECTION (CPU_CMD_MASK_CAPTURE | 0x001D) + +/* Description: Set Aspect Ratio + IN[0] - task handle. Handle of the task to start + IN[1] - AspectRatio + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_ASPECT_RATIO (CPU_CMD_MASK_CAPTURE | 0x001E) + +/* Description: Set Skip Input Frame + IN[0] - task handle. Handle of the task to start + IN[1] - skip input frames + ReturnCode - One of the ERR_CAPTURE_... */ +#define CX18_CPU_SET_SKIP_INPUT_FRAME (CPU_CMD_MASK_CAPTURE | 0x001F) + +/* Description: Set sliced VBI parameters - + Note This API will only apply to MPEG and Sliced VBI Channels + IN[0] - Task handle + IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext + IN[2] - start / stop line + bit[15:0] start line number + bit[31:16] stop line number + IN[3] - number of output frames per interrupt + IN[4] - VBI insertion mode + bit 0: output user data, 1 - enable + bit 1: output private stream, 1 - enable + bit 2: mux option, 0 - in GOP, 1 - in picture + bit[7:0] private stream ID + IN[5] - insertion period while mux option is in picture + ReturnCode - VBI data offset */ +#define CX18_CPU_SET_SLICED_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0020) + +/* Description: Set the user data place holder + IN[0] - type of data (0 for user) + IN[1] - Stuffing period + IN[2] - ID data size in word (less than 10) + IN[3] - Pointer to ID buffer */ +#define CX18_CPU_SET_USERDATA_PLACE_HOLDER (CPU_CMD_MASK_CAPTURE | 0x0021) + + +/* Description: + In[0] Task Handle + return parameter: + Out[0] Reserved + Out[1] Video PTS bit[32:2] of last output video frame. + Out[2] Video PTS bit[ 1:0] of last output video frame. + Out[3] Hardware Video PTS counter bit[31:0], + these bits get incremented on every 90kHz clock tick. + Out[4] Hardware Video PTS counter bit32, + these bits get incremented on every 90kHz clock tick. + ReturnCode */ +#define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022) + +/* Below is the list of commands related to the data exchange */ +#define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000) + +/* Description: This command provides the physical base address of the local + DDR as viewed by EPU + IN[0] - Physical offset where EPU has the local DDR mapped + ReturnCode - One of the ERR_DE_... */ +#define CPU_CMD_DE_SetBase (CPU_CMD_MASK_DE | 0x0001) + +/* Description: This command provides the offsets in the device memory where + the 2 cx18_mdl_ack blocks reside + IN[0] - Task handle. Handle of the task to start + IN[1] - Offset of the first cx18_mdl_ack from the beginning of the + local DDR. + IN[2] - Offset of the second cx18_mdl_ack from the beginning of the + local DDR. + ReturnCode - One of the ERR_DE_... */ +#define CX18_CPU_DE_SET_MDL_ACK (CPU_CMD_MASK_DE | 0x0002) + +/* Description: This command provides the offset to a Memory Descriptor List + IN[0] - Task handle. Handle of the task to start + IN[1] - Offset of the MDL from the beginning of the local DDR. + IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1] + IN[3] - Buffer ID + IN[4] - Total buffer length + ReturnCode - One of the ERR_DE_... */ +#define CX18_CPU_DE_SET_MDL (CPU_CMD_MASK_DE | 0x0005) + +/* Description: This command requests return of all current Memory + Descriptor Lists to the driver + IN[0] - Task handle. Handle of the task to start + ReturnCode - One of the ERR_DE_... */ +/* #define CX18_CPU_DE_ReleaseMDL (CPU_CMD_MASK_DE | 0x0006) */ + +/* Description: This command signals the cpu that the dat buffer has been + consumed and ready for re-use. + IN[0] - Task handle. Handle of the task + IN[1] - Offset of the data block from the beginning of the local DDR. + IN[2] - Number of bytes in the data block + ReturnCode - One of the ERR_DE_... */ +/* #define CX18_CPU_DE_RELEASE_BUFFER (CPU_CMD_MASK_DE | 0x0007) */ + +/* No Error / Success */ +#define CNXT_OK 0x000000 + +/* Received unknown command */ +#define CXERR_UNK_CMD 0x000001 + +/* First parameter in the command is invalid */ +#define CXERR_INVALID_PARAM1 0x000002 + +/* Second parameter in the command is invalid */ +#define CXERR_INVALID_PARAM2 0x000003 + +/* Device interface is not open/found */ +#define CXERR_DEV_NOT_FOUND 0x000004 + +/* Requested function is not implemented/available */ +#define CXERR_NOTSUPPORTED 0x000005 + +/* Invalid pointer is provided */ +#define CXERR_BADPTR 0x000006 + +/* Unable to allocate memory */ +#define CXERR_NOMEM 0x000007 + +/* Object/Link not found */ +#define CXERR_LINK 0x000008 + +/* Device busy, command cannot be executed */ +#define CXERR_BUSY 0x000009 + +/* File/device/handle is not open. */ +#define CXERR_NOT_OPEN 0x00000A + +/* Value is out of range */ +#define CXERR_OUTOFRANGE 0x00000B + +/* Buffer overflow */ +#define CXERR_OVERFLOW 0x00000C + +/* Version mismatch */ +#define CXERR_BADVER 0x00000D + +/* Operation timed out */ +#define CXERR_TIMEOUT 0x00000E + +/* Operation aborted */ +#define CXERR_ABORT 0x00000F + +/* Specified I2C device not found for read/write */ +#define CXERR_I2CDEV_NOTFOUND 0x000010 + +/* Error in I2C data xfer (but I2C device is present) */ +#define CXERR_I2CDEV_XFERERR 0x000011 + +/* Chanel changing component not ready */ +#define CXERR_CHANNELNOTREADY 0x000012 + +/* PPU (Presensation/Decoder) mail box is corrupted */ +#define CXERR_PPU_MB_CORRUPT 0x000013 + +/* CPU (Capture/Encoder) mail box is corrupted */ +#define CXERR_CPU_MB_CORRUPT 0x000014 + +/* APU (Audio) mail box is corrupted */ +#define CXERR_APU_MB_CORRUPT 0x000015 + +/* Unable to open file for reading */ +#define CXERR_FILE_OPEN_READ 0x000016 + +/* Unable to open file for writing */ +#define CXERR_FILE_OPEN_WRITE 0x000017 + +/* Unable to find the I2C section specified */ +#define CXERR_I2C_BADSECTION 0x000018 + +/* Error in I2C data xfer (but I2C device is present) */ +#define CXERR_I2CDEV_DATALOW 0x000019 + +/* Error in I2C data xfer (but I2C device is present) */ +#define CXERR_I2CDEV_CLOCKLOW 0x00001A + +/* No Interrupt received from HW (for I2C access) */ +#define CXERR_NO_HW_I2C_INTR 0x00001B + +/* RPU is not ready to accept commands! */ +#define CXERR_RPU_NOT_READY 0x00001C + +/* RPU is not ready to accept commands! */ +#define CXERR_RPU_NO_ACK 0x00001D + +/* The are no buffers ready. Try again soon! */ +#define CXERR_NODATA_AGAIN 0x00001E + +/* The stream is stopping. Function not alllowed now! */ +#define CXERR_STOPPING_STATUS 0x00001F + +/* Trying to access hardware when the power is turned OFF */ +#define CXERR_DEVPOWER_OFF 0x000020 + +#endif /* CX23418_H */ diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index 1fd326fe4..7bf14c9a1 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -1,6 +1,7 @@ config VIDEO_CX23885 tristate "Conexant cx23885 (2388x successor) support" depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT + depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT select FW_LOADER select VIDEO_BTCX @@ -8,14 +9,17 @@ config VIDEO_CX23885 select VIDEO_TVEEPROM select VIDEO_IR select VIDEOBUF_DVB - select DVB_TUNER_MT2131 if !DVB_FE_CUSTOMISE + select VIDEO_CX25840 + select VIDEO_CX2341X + select DVB_DIB7000P if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_PLL if !DVB_FE_CUSTOMISE - select TUNER_XC2028 if !DVB_FE_CUSTOMIZE - select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE - select DVB_TDA18271 if !DVB_FE_CUSTOMIZE - select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/linux/drivers/media/video/cx23885/Makefile b/linux/drivers/media/video/cx23885/Makefile index 32c90be50..29c23b44c 100644 --- a/linux/drivers/media/video/cx23885/Makefile +++ b/linux/drivers/media/video/cx23885/Makefile @@ -1,8 +1,9 @@ -cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o +cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.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 diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c new file mode 100644 index 000000000..1585db931 --- /dev/null +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -0,0 +1,1772 @@ +/* + * + * Support for a cx23417 mpeg encoder via cx23885 host port. + * + * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> + * (c) 2008 Steven Toth <stoth@hauppauge.com> + * - CX23885/7/8 support + * + * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <media/v4l2-common.h> +#include <media/cx2341x.h> + +#include "cx23885.h" +#include "media/cx2341x.h" + +#define CX23885_FIRM_IMAGE_SIZE 376836 +#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" + +static unsigned int mpegbufs = 32; +module_param(mpegbufs, int, 0644); +MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); +static unsigned int mpeglines = 32; +module_param(mpeglines, int, 0644); +MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); +static unsigned int mpeglinesize = 512; +module_param(mpeglinesize, int, 0644); +MODULE_PARM_DESC(mpeglinesize, + "number of bytes in each line of an MPEG buffer, range 512-1024"); + +static unsigned int v4l_debug; +module_param(v4l_debug, int, 0644); +MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); + +#define dprintk(level, fmt, arg...)\ + do { if (v4l_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg);\ + } while (0) + +static struct cx23885_tvnorm cx23885_tvnorms[] = { + { + .name = "NTSC-M", + .id = V4L2_STD_NTSC_M, + }, { + .name = "NTSC-JP", + .id = V4L2_STD_NTSC_M_JP, + }, { + .name = "PAL-BG", + .id = V4L2_STD_PAL_BG, + }, { + .name = "PAL-DK", + .id = V4L2_STD_PAL_DK, + }, { + .name = "PAL-I", + .id = V4L2_STD_PAL_I, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + }, { + .name = "PAL-N", + .id = V4L2_STD_PAL_N, + }, { + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + }, { + .name = "PAL-60", + .id = V4L2_STD_PAL_60, + }, { + .name = "SECAM-L", + .id = V4L2_STD_SECAM_L, + }, { + .name = "SECAM-DK", + .id = V4L2_STD_SECAM_DK, + } +}; + +/* ------------------------------------------------------------------ */ +enum cx23885_capture_type { + CX23885_MPEG_CAPTURE, + CX23885_RAW_CAPTURE, + CX23885_RAW_PASSTHRU_CAPTURE +}; +enum cx23885_capture_bits { + CX23885_RAW_BITS_NONE = 0x00, + CX23885_RAW_BITS_YUV_CAPTURE = 0x01, + CX23885_RAW_BITS_PCM_CAPTURE = 0x02, + CX23885_RAW_BITS_VBI_CAPTURE = 0x04, + CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08, + CX23885_RAW_BITS_TO_HOST_CAPTURE = 0x10 +}; +enum cx23885_capture_end { + CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */ + CX23885_END_NOW, /* stop immediately, no irq */ +}; +enum cx23885_framerate { + CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */ + CX23885_FRAMERATE_PAL_25 /* PAL: 25fps */ +}; +enum cx23885_stream_port { + CX23885_OUTPUT_PORT_MEMORY, + CX23885_OUTPUT_PORT_STREAMING, + CX23885_OUTPUT_PORT_SERIAL +}; +enum cx23885_data_xfer_status { + CX23885_MORE_BUFFERS_FOLLOW, + CX23885_LAST_BUFFER, +}; +enum cx23885_picture_mask { + CX23885_PICTURE_MASK_NONE, + CX23885_PICTURE_MASK_I_FRAMES, + CX23885_PICTURE_MASK_I_P_FRAMES = 0x3, + CX23885_PICTURE_MASK_ALL_FRAMES = 0x7, +}; +enum cx23885_vbi_mode_bits { + CX23885_VBI_BITS_SLICED, + CX23885_VBI_BITS_RAW, +}; +enum cx23885_vbi_insertion_bits { + CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, + CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, + CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, + CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, + CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, +}; +enum cx23885_dma_unit { + CX23885_DMA_BYTES, + CX23885_DMA_FRAMES, +}; +enum cx23885_dma_transfer_status_bits { + CX23885_DMA_TRANSFER_BITS_DONE = 0x01, + CX23885_DMA_TRANSFER_BITS_ERROR = 0x04, + CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10, +}; +enum cx23885_pause { + CX23885_PAUSE_ENCODING, + CX23885_RESUME_ENCODING, +}; +enum cx23885_copyright { + CX23885_COPYRIGHT_OFF, + CX23885_COPYRIGHT_ON, +}; +enum cx23885_notification_type { + CX23885_NOTIFICATION_REFRESH, +}; +enum cx23885_notification_status { + CX23885_NOTIFICATION_OFF, + CX23885_NOTIFICATION_ON, +}; +enum cx23885_notification_mailbox { + CX23885_NOTIFICATION_NO_MAILBOX = -1, +}; +enum cx23885_field1_lines { + CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */ + CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */ + CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */ +}; +enum cx23885_field2_lines { + CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */ + CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */ + CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */ +}; +enum cx23885_custom_data_type { + CX23885_CUSTOM_EXTENSION_USR_DATA, + CX23885_CUSTOM_PRIVATE_PACKET, +}; +enum cx23885_mute { + CX23885_UNMUTE, + CX23885_MUTE, +}; +enum cx23885_mute_video_mask { + CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00, + CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000, + CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000, +}; +enum cx23885_mute_video_shift { + CX23885_MUTE_VIDEO_V_SHIFT = 8, + CX23885_MUTE_VIDEO_U_SHIFT = 16, + CX23885_MUTE_VIDEO_Y_SHIFT = 24, +}; + +/* defines below are from ivtv-driver.h */ +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF + +/* Firmware API commands */ +#define IVTV_API_STD_TIMEOUT 500 + +/* Registers */ +/* IVTV_REG_OFFSET */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +/**** Bit definitions for MC417_RWD and MC417_OEN registers *** + bits 31-16 ++-----------+ +| Reserved | ++-----------+ + bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 ++-------+-------+-------+-------+-------+-------+-------+-------+ +| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0| ++-------+-------+-------+-------+-------+-------+-------+-------+ + bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 ++-------+-------+-------+-------+-------+-------+-------+-------+ +|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0| ++-------+-------+-------+-------+-------+-------+-------+-------+ +***/ +#define MC417_MIWR 0x8000 +#define MC417_MIRD 0x4000 +#define MC417_MICS 0x2000 +#define MC417_MIRDY 0x1000 +#define MC417_MIADDR 0x0F00 +#define MC417_MIDATA 0x00FF + +/* MIADDR* nibble definitions */ +#define MCI_MEMORY_DATA_BYTE0 0x000 +#define MCI_MEMORY_DATA_BYTE1 0x100 +#define MCI_MEMORY_DATA_BYTE2 0x200 +#define MCI_MEMORY_DATA_BYTE3 0x300 +#define MCI_MEMORY_ADDRESS_BYTE2 0x400 +#define MCI_MEMORY_ADDRESS_BYTE1 0x500 +#define MCI_MEMORY_ADDRESS_BYTE0 0x600 +#define MCI_REGISTER_DATA_BYTE0 0x800 +#define MCI_REGISTER_DATA_BYTE1 0x900 +#define MCI_REGISTER_DATA_BYTE2 0xA00 +#define MCI_REGISTER_DATA_BYTE3 0xB00 +#define MCI_REGISTER_ADDRESS_BYTE0 0xC00 +#define MCI_REGISTER_ADDRESS_BYTE1 0xD00 +#define MCI_REGISTER_MODE 0xE00 + +/* Read and write modes */ +#define MCI_MODE_REGISTER_READ 0 +#define MCI_MODE_REGISTER_WRITE 1 +#define MCI_MODE_MEMORY_READ 0 +#define MCI_MODE_MEMORY_WRITE 0x40 + +/*** Bit definitions for MC417_CTL register **** + bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0 ++--------+-------------+--------+--------------+------------+ +|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN| ++--------+-------------+--------+--------------+------------+ +***/ +#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030) +#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006) +#define MC417_UART_GPIO_EN 0x00000001 + +/* Values for speed control */ +#define MC417_SPD_CTL_SLOW 0x1 +#define MC417_SPD_CTL_MEDIUM 0x0 +#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */ + +/* Values for GPIO select */ +#define MC417_GPIO_SEL_GPIO3 0x3 +#define MC417_GPIO_SEL_GPIO2 0x2 +#define MC417_GPIO_SEL_GPIO1 0x1 +#define MC417_GPIO_SEL_GPIO0 0x0 + +void cx23885_mc417_init(struct cx23885_dev *dev) +{ + u32 regval; + + dprintk(2, "%s()\n", __func__); + + /* Configure MC417_CTL register to defaults. */ + regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST) | + MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3) | + MC417_UART_GPIO_EN; + cx_write(MC417_CTL, regval); + + /* Configure MC417_OEN to defaults. */ + regval = MC417_MIRDY; + cx_write(MC417_OEN, regval); + + /* Configure MC417_RWD to defaults. */ + regval = MC417_MIWR | MC417_MIRD | MC417_MICS; + cx_write(MC417_RWD, regval); +} + +static int mc417_wait_ready(struct cx23885_dev *dev) +{ + u32 mi_ready; + unsigned long timeout = jiffies + msecs_to_jiffies(1); + + for (;;) { + mi_ready = cx_read(MC417_RWD) & MC417_MIRDY; + if (mi_ready != 0) + return 0; + if (time_after(jiffies, timeout)) + return -1; + udelay(1); + } +} + +static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) +{ + u32 regval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 | + (value & 0x000000FF); + cx_write(MC417_RWD, regval); + + /* Transition CS/WR to effect write transaction across bus. */ + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 | + ((value >> 8) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 | + ((value >> 16) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 | + ((value >> 24) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 | + (address & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Indicate that this is a write. */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE | + MCI_MODE_REGISTER_WRITE; + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + return mc417_wait_ready(dev); +} + +static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) +{ + int retval; + u32 regval; + u32 tempval; + u32 dataval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 | + ((address & 0x00FF)); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Indicate that this is a register read. */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE | + MCI_MODE_REGISTER_READ; + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + retval = mc417_wait_ready(dev); + + /* switch the DAT0-7 GPIO[10:3] to input mode */ + cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA); + + /* Read data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0; + cx_write(MC417_RWD, regval); + + /* Transition RD to effect read transaction across bus. + * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)? + * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its + * input only...) + */ + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0; + cx_write(MC417_RWD, regval); + + /* Collect byte */ + tempval = cx_read(MC417_RWD); + dataval = tempval & 0x000000FF; + + /* Bring CS and RD high. */ + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 8); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 16); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 24); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + *value = dataval; + + return retval; +} + +int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value) +{ + u32 regval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 | + (value & 0x000000FF); + cx_write(MC417_RWD, regval); + + /* Transition CS/WR to effect write transaction across bus. */ + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 | + ((value >> 8) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 | + ((value >> 16) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 | + ((value >> 24) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 | + MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 | + (address & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + return mc417_wait_ready(dev); +} + +int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) +{ + int retval; + u32 regval; + u32 tempval; + u32 dataval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write address byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 | + MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 | + (address & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + retval = mc417_wait_ready(dev); + + /* switch the DAT0-7 GPIO[10:3] to input mode */ + cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA); + + /* Read data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3; + cx_write(MC417_RWD, regval); + + /* Transition RD to effect read transaction across bus. */ + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3; + cx_write(MC417_RWD, regval); + + /* Collect byte */ + tempval = cx_read(MC417_RWD); + dataval = ((tempval & 0x000000FF) << 24); + + /* Bring CS and RD high. */ + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 16); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 8); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= (tempval & 0x000000FF); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + *value = dataval; + + return retval; +} + +/* ------------------------------------------------------------------ */ + +/* MPEG encoder API */ +char *cmd_to_str(int cmd) +{ + switch (cmd) { + case CX2341X_ENC_PING_FW: + return "PING_FW"; + case CX2341X_ENC_START_CAPTURE: + return "START_CAPTURE"; + case CX2341X_ENC_STOP_CAPTURE: + return "STOP_CAPTURE"; + case CX2341X_ENC_SET_AUDIO_ID: + return "SET_AUDIO_ID"; + case CX2341X_ENC_SET_VIDEO_ID: + return "SET_VIDEO_ID"; + case CX2341X_ENC_SET_PCR_ID: + return "SET_PCR_PID"; + case CX2341X_ENC_SET_FRAME_RATE: + return "SET_FRAME_RATE"; + case CX2341X_ENC_SET_FRAME_SIZE: + return "SET_FRAME_SIZE"; + case CX2341X_ENC_SET_BIT_RATE: + return "SET_BIT_RATE"; + case CX2341X_ENC_SET_GOP_PROPERTIES: + return "SET_GOP_PROPERTIES"; + case CX2341X_ENC_SET_ASPECT_RATIO: + return "SET_ASPECT_RATIO"; + case CX2341X_ENC_SET_DNR_FILTER_MODE: + return "SET_DNR_FILTER_PROPS"; + case CX2341X_ENC_SET_DNR_FILTER_PROPS: + return "SET_DNR_FILTER_PROPS"; + case CX2341X_ENC_SET_CORING_LEVELS: + return "SET_CORING_LEVELS"; + case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: + return "SET_SPATIAL_FILTER_TYPE"; + case CX2341X_ENC_SET_VBI_LINE: + return "SET_VBI_LINE"; + case CX2341X_ENC_SET_STREAM_TYPE: + return "SET_STREAM_TYPE"; + case CX2341X_ENC_SET_OUTPUT_PORT: + return "SET_OUTPUT_PORT"; + case CX2341X_ENC_SET_AUDIO_PROPERTIES: + return "SET_AUDIO_PROPERTIES"; + case CX2341X_ENC_HALT_FW: + return "HALT_FW"; + case CX2341X_ENC_GET_VERSION: + return "GET_VERSION"; + case CX2341X_ENC_SET_GOP_CLOSURE: + return "SET_GOP_CLOSURE"; + case CX2341X_ENC_GET_SEQ_END: + return "GET_SEQ_END"; + case CX2341X_ENC_SET_PGM_INDEX_INFO: + return "SET_PGM_INDEX_INFO"; + case CX2341X_ENC_SET_VBI_CONFIG: + return "SET_VBI_CONFIG"; + case CX2341X_ENC_SET_DMA_BLOCK_SIZE: + return "SET_DMA_BLOCK_SIZE"; + case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10: + return "GET_PREV_DMA_INFO_MB_10"; + case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9: + return "GET_PREV_DMA_INFO_MB_9"; + case CX2341X_ENC_SCHED_DMA_TO_HOST: + return "SCHED_DMA_TO_HOST"; + case CX2341X_ENC_INITIALIZE_INPUT: + return "INITIALIZE_INPUT"; + case CX2341X_ENC_SET_FRAME_DROP_RATE: + return "SET_FRAME_DROP_RATE"; + case CX2341X_ENC_PAUSE_ENCODER: + return "PAUSE_ENCODER"; + case CX2341X_ENC_REFRESH_INPUT: + return "REFRESH_INPUT"; + case CX2341X_ENC_SET_COPYRIGHT: + return "SET_COPYRIGHT"; + case CX2341X_ENC_SET_EVENT_NOTIFICATION: + return "SET_EVENT_NOTIFICATION"; + case CX2341X_ENC_SET_NUM_VSYNC_LINES: + return "SET_NUM_VSYNC_LINES"; + case CX2341X_ENC_SET_PLACEHOLDER: + return "SET_PLACEHOLDER"; + case CX2341X_ENC_MUTE_VIDEO: + return "MUTE_VIDEO"; + case CX2341X_ENC_MUTE_AUDIO: + return "MUTE_AUDIO"; + case CX2341X_ENC_MISC: + return "MISC"; + default: + return "UNKNOWN"; + } +} + +static int cx23885_mbox_func(void *priv, + u32 command, + int in, + int out, + u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct cx23885_dev *dev = priv; + unsigned long timeout; + u32 value, flag, retval = 0; + int i; + + dprintk(3, "%s: command(0x%X) = %s\n", __func__, command, + cmd_to_str(command)); + + /* this may not be 100% safe if we can't read any memory location + without side effects */ + mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value); + if (value != 0x12345678) { + printk(KERN_ERR + "Firmware and/or mailbox pointer not initialized " + "or corrupted, signature = 0x%x, cmd = %s\n", value, + cmd_to_str(command)); + return -1; + } + + /* This read looks at 32 bits, but flag is only 8 bits. + * Seems we also bail if CMD or TIMEOUT bytes are set??? + */ + mc417_memory_read(dev, dev->cx23417_mailbox, &flag); + if (flag) { + printk(KERN_ERR "ERROR: Mailbox appears to be in use " + "(%x), cmd = %s\n", flag, cmd_to_str(command)); + return -1; + } + + flag |= 1; /* tell 'em we're working on it */ + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + /* write command + args + fill remaining with zeros */ + /* command code */ + mc417_memory_write(dev, dev->cx23417_mailbox + 1, command); + mc417_memory_write(dev, dev->cx23417_mailbox + 3, + IVTV_API_STD_TIMEOUT); /* timeout */ + for (i = 0; i < in; i++) { + mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]); + dprintk(3, "API Input %d = %d\n", i, data[i]); + } + for (; i < CX2341X_MBOX_MAX_DATA; i++) + mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0); + + flag |= 3; /* tell 'em we're done writing */ + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + /* wait for firmware to handle the API command */ + timeout = jiffies + msecs_to_jiffies(10); + for (;;) { + mc417_memory_read(dev, dev->cx23417_mailbox, &flag); + if (0 != (flag & 4)) + break; + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "ERROR: API Mailbox timeout\n"); + return -1; + } + udelay(10); + } + + /* read output values */ + for (i = 0; i < out; i++) { + mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i); + dprintk(3, "API Output %d = %d\n", i, data[i]); + } + + mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval); + dprintk(3, "API result = %d\n", retval); + + flag = 0; + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + return retval; +} + +/* We don't need to call the API often, so using just one + * mailbox will probably suffice + */ +static int cx23885_api_cmd(struct cx23885_dev *dev, + u32 command, + u32 inputcnt, + u32 outputcnt, + ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i, err; + + dprintk(3, "%s() cmds = 0x%08x\n", __func__, command); + + va_start(vargs, outputcnt); + for (i = 0; i < inputcnt; i++) + data[i] = va_arg(vargs, int); + + err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data); + for (i = 0; i < outputcnt; i++) { + int *vptr = va_arg(vargs, int *); + *vptr = data[i]; + } + va_end(vargs); + + return err; +} + +static int cx23885_find_mailbox(struct cx23885_dev *dev) +{ + u32 signature[4] = { + 0x12345678, 0x34567812, 0x56781234, 0x78123456 + }; + int signaturecnt = 0; + u32 value; + int i; + + dprintk(2, "%s()\n", __func__); + + for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) { + mc417_memory_read(dev, i, &value); + if (value == signature[signaturecnt]) + signaturecnt++; + else + signaturecnt = 0; + if (4 == signaturecnt) { + dprintk(1, "Mailbox signature found at 0x%x\n", i+1); + return i+1; + } + } + printk(KERN_ERR "Mailbox signature values not found!\n"); + return -1; +} + +static int cx23885_load_firmware(struct cx23885_dev *dev) +{ + static const unsigned char magic[8] = { + 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa + }; + const struct firmware *firmware; + int i, retval = 0; + u32 value = 0; + u32 gpio_output = 0; + u32 checksum = 0; + u32 *dataptr; + + dprintk(2, "%s()\n", __func__); + + /* Save GPIO settings before reset of APU */ + retval |= mc417_memory_read(dev, 0x9020, &gpio_output); + retval |= mc417_memory_read(dev, 0x900C, &value); + + retval = mc417_register_write(dev, + IVTV_REG_VPU, 0xFFFFFFED); + retval |= mc417_register_write(dev, + IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); + retval |= mc417_register_write(dev, + IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800); + retval |= mc417_register_write(dev, + IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); + retval |= mc417_register_write(dev, + IVTV_REG_APU, 0); + + if (retval != 0) { + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return -1; + } + + retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME, + &dev->pci->dev); + + if (retval != 0) { + printk(KERN_ERR + "ERROR: Hotplug firmware request failed (%s).\n", + CX2341X_FIRM_ENC_FILENAME); + printk(KERN_ERR "Please fix your hotplug setup, the board will " + "not work without firmware loaded!\n"); + return -1; + } + + if (firmware->size != CX23885_FIRM_IMAGE_SIZE) { + printk(KERN_ERR "ERROR: Firmware size mismatch " + "(have %zd, expected %d)\n", + firmware->size, CX23885_FIRM_IMAGE_SIZE); + release_firmware(firmware); + return -1; + } + + if (0 != memcmp(firmware->data, magic, 8)) { + printk(KERN_ERR + "ERROR: Firmware magic mismatch, wrong file?\n"); + release_firmware(firmware); + return -1; + } + + /* transfer to the chip */ + dprintk(2, "Loading firmware ...\n"); + dataptr = (u32 *)firmware->data; + for (i = 0; i < (firmware->size >> 2); i++) { + value = *dataptr; + checksum += ~value; + if (mc417_memory_write(dev, i, value) != 0) { + printk(KERN_ERR "ERROR: Loading firmware failed!\n"); + release_firmware(firmware); + return -1; + } + dataptr++; + } + + /* read back to verify with the checksum */ + dprintk(1, "Verifying firmware ...\n"); + for (i--; i >= 0; i--) { + if (mc417_memory_read(dev, i, &value) != 0) { + printk(KERN_ERR "ERROR: Reading firmware failed!\n"); + release_firmware(firmware); + return -1; + } + checksum -= ~value; + } + if (checksum) { + printk(KERN_ERR + "ERROR: Firmware load failed (checksum mismatch).\n"); + release_firmware(firmware); + return -1; + } + release_firmware(firmware); + dprintk(1, "Firmware upload successful.\n"); + + retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, + IVTV_CMD_HW_BLOCKS_RST); + + /* Restore GPIO settings, make sure EIO14 is enabled as an output. */ + dprintk(2, "%s: GPIO output EIO 0-15 was = 0x%x\n", + __func__, gpio_output); + /* Power-up seems to have GPIOs AFU. This was causing digital side + * to fail at power-up. Seems GPIOs should be set to 0x10ff0411 at + * power-up. + * gpio_output |= (1<<14); + */ + /* Note: GPIO14 is specific to the HVR1800 here */ + gpio_output = 0x10ff0411 | (1<<14); + retval |= mc417_register_write(dev, 0x9020, gpio_output | (1<<14)); + dprintk(2, "%s: GPIO output EIO 0-15 now = 0x%x\n", + __func__, gpio_output); + + dprintk(1, "%s: GPIO value EIO 0-15 was = 0x%x\n", + __func__, value); + value |= (1<<14); + dprintk(1, "%s: GPIO value EIO 0-15 now = 0x%x\n", + __func__, value); + retval |= mc417_register_write(dev, 0x900C, value); + + retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); + retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8); + + if (retval < 0) + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return 0; +} + +void cx23885_417_check_encoder(struct cx23885_dev *dev) +{ + u32 status, seq; + + status = seq = 0; + cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq); + dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq); +} + +static void cx23885_codec_settings(struct cx23885_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + /* assign frame size */ + cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, + dev->ts1.height, dev->ts1.width); + + dev->mpeg_params.width = dev->ts1.width; + dev->mpeg_params.height = dev->ts1.height; + dev->mpeg_params.is_50hz = + (dev->encodernorm.id & V4L2_STD_625_50) != 0; + + cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params); + + cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); + cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); +} + +static int cx23885_initialize_codec(struct cx23885_dev *dev) +{ + int version; + int retval; + u32 i, data[7]; + + dprintk(1, "%s()\n", __func__); + + retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ + if (retval < 0) { + dprintk(2, "%s() PING OK\n", __func__); + retval = cx23885_load_firmware(dev); + if (retval < 0) { + printk(KERN_ERR "%s() f/w load failed\n", __func__); + return retval; + } + dev->cx23417_mailbox = cx23885_find_mailbox(dev); + if (dev->cx23417_mailbox < 0) { + printk(KERN_ERR "%s() mailbox < 0, error\n", + __func__); + return -1; + } + retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); + if (retval < 0) { + printk(KERN_ERR + "ERROR: cx23417 firmware ping failed!\n"); + return -1; + } + retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, + &version); + if (retval < 0) { + printk(KERN_ERR "ERROR: cx23417 firmware get encoder :" + "version failed!\n"); + return -1; + } + dprintk(1, "cx23417 firmware version is 0x%08x\n", version); + msleep(200); + } + + cx23885_codec_settings(dev); + msleep(60); + + cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, + CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115); + cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, + CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0); + + /* Setup to capture VBI */ + data[0] = 0x0001BD00; + data[1] = 1; /* frames per interrupt */ + data[2] = 4; /* total bufs */ + data[3] = 0x91559155; /* start codes */ + data[4] = 0x206080C0; /* stop codes */ + data[5] = 6; /* lines */ + data[6] = 64; /* BPL */ + + cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], + data[2], data[3], data[4], data[5], data[6]); + + for (i = 2; i <= 24; i++) { + int valid; + + valid = ((i >= 19) && (i <= 21)); + cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i, + valid, 0 , 0, 0); + cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, + i | 0x80000000, valid, 0, 0, 0); + } + + cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE); + msleep(60); + + /* initialize the video input */ + cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); + msleep(60); + + /* Enable VIP style pixel invalidation so we work with scaled mode */ + mc417_memory_write(dev, 2120, 0x00000080); + + /* start capturing to the host interface */ + cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, + CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE); + msleep(10); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int bb_buf_setup(struct videobuf_queue *q, + unsigned int *count, unsigned int *size) +{ + struct cx23885_fh *fh = q->priv_data; + + fh->dev->ts1.ts_packet_size = mpeglinesize; + fh->dev->ts1.ts_packet_count = mpeglines; + + *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; + *count = mpegbufs; + + return 0; +} + +static int bb_buf_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct cx23885_fh *fh = q->priv_data; + return cx23885_buf_prepare(q, &fh->dev->ts1, + (struct cx23885_buffer *)vb, + field); +} + +static void bb_buf_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct cx23885_fh *fh = q->priv_data; + cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb); +} + +static void bb_buf_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + cx23885_free_buffer(q, (struct cx23885_buffer *)vb); +} + +static struct videobuf_queue_ops cx23885_qops = { + .buf_setup = bb_buf_setup, + .buf_prepare = bb_buf_prepare, + .buf_queue = bb_buf_queue, + .buf_release = bb_buf_release, +}; + +/* ------------------------------------------------------------------ */ + +static const u32 *ctrl_classes[] = { + cx2341x_mpeg_ctrls, + NULL +}; + +static int cx23885_queryctrl(struct cx23885_dev *dev, + struct v4l2_queryctrl *qctrl) +{ + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + /* MPEG V4L2 controls */ + if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + + return 0; +} + +static int cx23885_querymenu(struct cx23885_dev *dev, + struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + cx23885_queryctrl(dev, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + cx2341x_ctrl_get_menu(qmenu->id)); +} + +int cx23885_do_ioctl(struct inode *inode, struct file *file, int radio, + struct cx23885_dev *dev, unsigned int cmd, void *arg, + v4l2_kioctl driver_ioctl) +{ + int err; + + switch (cmd) { + /* ---------- tv norms ---------- */ + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = arg; + unsigned int i; + + i = e->index; + if (i >= ARRAY_SIZE(cx23885_tvnorms)) + return -EINVAL; + err = v4l2_video_std_construct(e, + cx23885_tvnorms[e->index].id, + cx23885_tvnorms[e->index].name); + e->index = i; + if (err < 0) + return err; + return 0; + } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + + *id = dev->encodernorm.id; + return 0; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) + if (*id & cx23885_tvnorms[i].id) + break; + if (i == ARRAY_SIZE(cx23885_tvnorms)) + return -EINVAL; + dev->encodernorm = cx23885_tvnorms[i]; +#if 0 + /* Notify the video decoder and other i2c clients. + * This will likely need to be enabled for non NTSC + * formats. + */ + cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_S_STD, id); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, id); +#endif + + return 0; + } + + /* ------ input switching ---------- */ + case VIDIOC_ENUMINPUT: + { + struct cx23885_input *input; + struct v4l2_input *i = arg; + unsigned int n; + + n = i->index; + if (n >= 4) + return -EINVAL; + input = &cx23885_boards[dev->board].input[n]; + if (input->type == 0) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = n; + /* FIXME + * strcpy(i->name, input->name); */ + strcpy(i->name, "unset"); + if (input->type == CX23885_VMUX_TELEVISION || + input->type == CX23885_VMUX_CABLE) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + for (n = 0; n < ARRAY_SIZE(cx23885_tvnorms); n++) + i->std |= cx23885_tvnorms[n].id; + return 0; + } + case VIDIOC_G_INPUT: + { + unsigned int *i = arg; + + *i = dev->input; + return 0; + } + case VIDIOC_S_INPUT: + { + unsigned int *i = arg; + + if (*i >= 4) + return -EINVAL; + + return 0; + } + + /* --- tuner ioctls ------------------------------------------ */ + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *t = arg; + + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Television"); + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); + + dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); + + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *t = arg; + + if (UNSET == dev->tuner_type) + return -EINVAL; + + /* Update the A/V core */ + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t); + + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + memset(f, 0, sizeof(*f)); + if (UNSET == dev->tuner_type) + return -EINVAL; + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + /* Assumption that tuner is always on bus 1 */ + cx23885_call_i2c_clients(&dev->i2c_bus[1], + VIDIOC_G_FREQUENCY, f); + + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + dprintk(1, "VIDIOC_S_FREQUENCY: dev type %d, f\n", + dev->tuner_type); + dprintk(1, "VIDIOC_S_FREQUENCY: f tuner %d, f type %d\n", + f->tuner, f->type); + if (UNSET == dev->tuner_type) + return -EINVAL; + if (f->tuner != 0) + return -EINVAL; + if (f->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + dev->freq = f->frequency; + + /* Assumption that tuner is always on bus 1 */ + cx23885_call_i2c_clients(&dev->i2c_bus[1], + VIDIOC_S_FREQUENCY, f); + return 0; + } + case VIDIOC_S_CTRL: + { + /* Update the A/V core */ + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, arg); + return 0; + } + default: + /* Convert V4L ioctl to V4L2 and call mpeg_do_ioctl + * (driver_ioctl) */ + return v4l_compat_translate_ioctl(inode, file, cmd, arg, + driver_ioctl); + } + + return 0; +} + +static int mpeg_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + struct cx23885_tsport *tsport = &dev->ts1; + + if (v4l_debug > 1) + v4l_print_ioctl(dev->name, cmd); + + switch (cmd) { + + /* --- capabilities ------------------------------------------ */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, dev->name); + strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->version = CX23885_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + 0; + if (UNSET != dev->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; + } + + /* --- capture ioctls ---------------------------------------- */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + index = f->index; + if (index != 0) + return -EINVAL; + + memset(f, 0, sizeof(*f)); + f->index = index; + strlcpy(f->description, "MPEG", sizeof(f->description)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; + return 0; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *f = arg; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + f->fmt.pix.width = dev->ts1.width; + f->fmt.pix.height = dev->ts1.height; + f->fmt.pix.field = fh->mpegq.field; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->mpegq.field); + return 0; + } + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = arg; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.sizeimage = + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->mpegq.field); + return 0; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *f = arg; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + return 0; + } + + /* --- streaming capture ------------------------------------- */ + case VIDIOC_REQBUFS: + return videobuf_reqbufs(&fh->mpegq, arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(&fh->mpegq, arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(&fh->mpegq, arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(&fh->mpegq, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + return videobuf_streamon(&fh->mpegq); + + case VIDIOC_STREAMOFF: + return videobuf_streamoff(&fh->mpegq); + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *f = arg; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, cmd); + } + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *f = arg; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + p = dev->mpeg_params; + err = cx2341x_ext_ctrls(&p, 0, f, cmd); + if (err == 0 && cmd == VIDIOC_S_EXT_CTRLS) { + err = cx2341x_update(dev, cx23885_mbox_func, + &dev->mpeg_params, &p); + dev->mpeg_params = p; + } + return err; + } + case VIDIOC_S_FREQUENCY: + { + cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); + cx23885_do_ioctl(inode, file, 0, dev, cmd, arg, + mpeg_do_ioctl); + cx23885_initialize_codec(dev); + + return 0; + } + case VIDIOC_LOG_STATUS: + { + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO + "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS, + NULL); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS, + NULL); + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS, + NULL); + cx2341x_log_status(&dev->mpeg_params, name); + printk(KERN_INFO + "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; + } + case VIDIOC_QUERYMENU: + return cx23885_querymenu(dev, arg); + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *c = arg; + + return cx23885_queryctrl(dev, c); + } + + default: + return cx23885_do_ioctl(inode, file, 0, dev, cmd, arg, + mpeg_do_ioctl); + } + return 0; +} + +static int mpeg_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); +} + +static int mpeg_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct cx23885_dev *h, *dev = NULL; + struct list_head *list; + struct cx23885_fh *fh; + + dprintk(2, "%s()\n", __func__); + + list_for_each(list, &cx23885_devlist) { + h = list_entry(list, struct cx23885_dev, devlist); + if (h->v4l_device->minor == minor) { + dev = h; + break; + } + } + + if (dev == NULL) + return -ENODEV; + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + file->private_data = fh; + fh->dev = dev; + + videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops, + &dev->pci->dev, &dev->ts1.slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx23885_buffer), + fh); + + return 0; +} + +static int mpeg_release(struct inode *inode, struct file *file) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + /* FIXME: Review this crap */ + /* Shut device down on last close */ + if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { + if (atomic_dec_return(&dev->v4l_reader_count) == 0) { + /* stop mpeg capture */ + cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); + + msleep(500); + cx23885_417_check_encoder(dev); + + cx23885_cancel_buffers(&fh->dev->ts1); + } + } + + if (fh->mpegq.streaming) + videobuf_streamoff(&fh->mpegq); + if (fh->mpegq.reading) + videobuf_read_stop(&fh->mpegq); + + videobuf_mmap_free(&fh->mpegq); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static ssize_t mpeg_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ + /* Start mpeg encoder on first read. */ + if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { + if (atomic_inc_return(&dev->v4l_reader_count) == 1) { + if (cx23885_initialize_codec(dev) < 0) + return -EINVAL; + } + } + + return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static unsigned int mpeg_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s\n", __func__); + + return videobuf_poll_stream(file, &fh->mpegq, wait); +} + +static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + return videobuf_mmap_mapper(&fh->mpegq, vma); +} + +static struct file_operations mpeg_fops = { + .owner = THIS_MODULE, + .open = mpeg_open, + .release = mpeg_release, + .read = mpeg_read, + .poll = mpeg_poll, + .mmap = mpeg_mmap, + .ioctl = mpeg_ioctl, + .llseek = no_llseek, +}; + +static struct video_device cx23885_mpeg_template = { + .name = "cx23885", + .type = VID_TYPE_CAPTURE | + VID_TYPE_TUNER | + VID_TYPE_SCALES | + VID_TYPE_MPEG_ENCODER, + .fops = &mpeg_fops, + .minor = -1, +}; + +void cx23885_417_unregister(struct cx23885_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + if (dev->v4l_device) { + if (-1 != dev->v4l_device->minor) + video_unregister_device(dev->v4l_device); + else + video_device_release(dev->v4l_device); + dev->v4l_device = NULL; + } +} + +static struct video_device *cx23885_video_dev_alloc( + struct cx23885_tsport *tsport, + struct pci_dev *pci, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + struct cx23885_dev *dev = tsport->dev; + + dprintk(1, "%s()\n", __func__); + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, + type, cx23885_boards[tsport->dev->board].name); + vfd->dev = &pci->dev; + vfd->release = video_device_release; + return vfd; +} + +int cx23885_417_register(struct cx23885_dev *dev) +{ + /* FIXME: Port1 hardcoded here */ + int err = -ENODEV; + struct cx23885_tsport *tsport = &dev->ts1; + + dprintk(1, "%s()\n", __func__); + + if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER) + return err; + + /* Set default TV standard */ + dev->encodernorm = cx23885_tvnorms[0]; + + if (dev->encodernorm.id & V4L2_STD_525_60) + tsport->height = 480; + else + tsport->height = 576; + + tsport->width = 720; + cx2341x_fill_defaults(&dev->mpeg_params); + + dev->mpeg_params.port = CX2341X_PORT_SERIAL; + + /* Allocate and initialize V4L video device */ + dev->v4l_device = cx23885_video_dev_alloc(tsport, + dev->pci, &cx23885_mpeg_template, "mpeg"); + err = video_register_device(dev->v4l_device, + VFL_TYPE_GRABBER, -1); + if (err < 0) { + printk(KERN_INFO "%s: can't register mpeg device\n", dev->name); + return err; + } + + /* Initialize MC417 registers */ + cx23885_mc417_init(dev); + + printk(KERN_INFO "%s: registered device video%d [mpeg]\n", + dev->name, dev->v4l_device->minor & 0x1f); + + return 0; +} diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index 6f04004b7..df09897c1 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -74,6 +74,7 @@ struct cx23885_board cx23885_boards[] = { [CX23885_BOARD_HAUPPAUGE_HVR1800] = { .name = "Hauppauge WinTV-HVR1800", .porta = CX23885_ANALOG_VIDEO, + .portb = CX23885_MPEG_ENCODER, .portc = CX23885_MPEG_DVB, .tuner_type = TUNER_PHILIPS_TDA8290, .tuner_addr = 0x42, /* 0x84 >> 1 */ @@ -131,6 +132,18 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR1500", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1200] = { + .name = "Hauppauge WinTV-HVR1200", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1700] = { + .name = "Hauppauge WinTV-HVR1700", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1400] = { + .name = "Hauppauge WinTV-HVR1400", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -182,6 +195,22 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x7717, .card = CX23885_BOARD_HAUPPAUGE_HVR1500, + }, { + .subvendor = 0x0070, + .subdevice = 0x71d1, + .card = CX23885_BOARD_HAUPPAUGE_HVR1200, + }, { + .subvendor = 0x0070, + .subdevice = 0x71d3, + .card = CX23885_BOARD_HAUPPAUGE_HVR1200, + }, { + .subvendor = 0x0070, + .subdevice = 0x8101, + .card = CX23885_BOARD_HAUPPAUGE_HVR1700, + }, { + .subvendor = 0x0070, + .subdevice = 0x8010, + .card = CX23885_BOARD_HAUPPAUGE_HVR1400, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -221,6 +250,33 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) /* Make sure we support the board model */ switch (tv.model) { + case 71009: + /* WinTV-HVR1200 (PCIe, Retail, full height) + * DVB-T and basic analog */ + case 71359: + /* WinTV-HVR1200 (PCIe, OEM, half height) + * DVB-T and basic analog */ + case 71439: + /* WinTV-HVR1200 (PCIe, OEM, half height) + * DVB-T and basic analog */ + case 71449: + /* WinTV-HVR1200 (PCIe, OEM, full height) + * DVB-T and basic analog */ + case 71939: + /* WinTV-HVR1200 (PCIe, OEM, half height) + * DVB-T and basic analog */ + case 71949: + /* WinTV-HVR1200 (PCIe, OEM, full height) + * DVB-T and basic analog */ + case 71959: + /* WinTV-HVR1200 (PCIe, OEM, full height) + * DVB-T and basic analog */ + case 71979: + /* WinTV-HVR1200 (PCIe, OEM, half height) + * DVB-T and basic analog */ + case 71999: + /* WinTV-HVR1200 (PCIe, OEM, full height) + * DVB-T and basic analog */ case 76601: /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual channel ATSC and MPEG2 HW Encoder */ case 77001: /* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC and Basic analog */ case 77011: /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC and Basic analog */ @@ -236,6 +292,15 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 79561: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ case 79571: /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ATSC and Basic analog */ case 79671: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ + case 80019: + /* WinTV-HVR1400 (Express Card, Retail, IR, + * DVB-T and Basic analog */ + case 81509: + /* WinTV-HVR1700 (PCIe, OEM, No IR, half height) + * DVB-T and MPEG2 HW Encoder */ + case 81519: + /* WinTV-HVR1700 (PCIe, OEM, No IR, full height) + * DVB-T and MPEG2 HW Encoder */ break; default: printk("%s: warning: unknown hauppauge model #%d\n", dev->name, tv.model); @@ -307,6 +372,10 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* GPIO-15-18 cx23417 READY, CS, RD, WR */ /* GPIO-19 IR_RX */ + /* CX23417 GPIO's */ + /* EIO15 Zilog Reset */ + /* EIO14 S5H1409/CX24227 Reset */ + /* Force the TDA8295A into reset and back */ cx_set(GP0_IO, 0x00040004); mdelay(20); @@ -315,6 +384,50 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); mdelay(20); break; + case CX23885_BOARD_HAUPPAUGE_HVR1200: + /* GPIO-0 tda10048 demodulator reset */ + /* GPIO-2 tda18271 tuner reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; + case CX23885_BOARD_HAUPPAUGE_HVR1700: + /* GPIO-0 TDA10048 demodulator reset */ + /* GPIO-2 TDA8295A Reset */ + /* GPIO-3-10 cx23417 data0-7 */ + /* GPIO-11-14 cx23417 addr0-3 */ + /* GPIO-15-18 cx23417 READY, CS, RD, WR */ + + /* The following GPIO's are on the interna AVCore (cx25840) */ + /* GPIO-19 IR_RX */ + /* GPIO-20 IR_TX 416/DVBT Select */ + /* GPIO-21 IIS DAT */ + /* GPIO-22 IIS WCLK */ + /* GPIO-23 IIS BCLK */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; + case CX23885_BOARD_HAUPPAUGE_HVR1400: + /* GPIO-0 Dibcom7000p demodulator reset */ + /* GPIO-2 xc3028L tuner reset */ + /* GPIO-13 LED */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; } } @@ -325,6 +438,8 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1400: /* FIXME: Implement me */ break; } @@ -349,11 +464,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: + case CX23885_BOARD_HAUPPAUGE_HVR1400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0x80); break; case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -365,17 +483,45 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR1800: + /* Defaults for VID B - Analog encoder */ + /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ + ts1->gen_ctrl_val = 0x10e; + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + + /* APB_TSVALERR_POL (active low)*/ + ts1->vld_misc_val = 0x2000; + ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc); + + /* Defaults for VID C */ + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: - case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + case CX23885_BOARD_HAUPPAUGE_HVR1400: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; } + /* Certain boards support analog, or require the avcore to be + * loaded, ensure this happens. + */ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + request_module("cx25840"); + break; + } } /* ------------------------------------------------------------------ */ diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 309c17586..49d8620a3 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -191,25 +191,25 @@ static struct sram_channel cx23887_sram_channels[] = { static int cx23885_risc_decode(u32 risc) { static char *instr[16] = { - [ RISC_SYNC >> 28 ] = "sync", - [ RISC_WRITE >> 28 ] = "write", - [ RISC_WRITEC >> 28 ] = "writec", - [ RISC_READ >> 28 ] = "read", - [ RISC_READC >> 28 ] = "readc", - [ RISC_JUMP >> 28 ] = "jump", - [ RISC_SKIP >> 28 ] = "skip", - [ RISC_WRITERM >> 28 ] = "writerm", - [ RISC_WRITECM >> 28 ] = "writecm", - [ RISC_WRITECR >> 28 ] = "writecr", + [RISC_SYNC >> 28] = "sync", + [RISC_WRITE >> 28] = "write", + [RISC_WRITEC >> 28] = "writec", + [RISC_READ >> 28] = "read", + [RISC_READC >> 28] = "readc", + [RISC_JUMP >> 28] = "jump", + [RISC_SKIP >> 28] = "skip", + [RISC_WRITERM >> 28] = "writerm", + [RISC_WRITECM >> 28] = "writecm", + [RISC_WRITECR >> 28] = "writecr", }; static int incr[16] = { - [ RISC_WRITE >> 28 ] = 3, - [ RISC_JUMP >> 28 ] = 3, - [ RISC_SKIP >> 28 ] = 1, - [ RISC_SYNC >> 28 ] = 1, - [ RISC_WRITERM >> 28 ] = 3, - [ RISC_WRITECM >> 28 ] = 3, - [ RISC_WRITECR >> 28 ] = 4, + [RISC_WRITE >> 28] = 3, + [RISC_JUMP >> 28] = 3, + [RISC_SKIP >> 28] = 1, + [RISC_SYNC >> 28] = 1, + [RISC_WRITERM >> 28] = 3, + [RISC_WRITECM >> 28] = 3, + [RISC_WRITECR >> 28] = 4, }; static char *bits[] = { "12", "13", "14", "resync", @@ -519,6 +519,8 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *p /* Transport bus init dma queue - Common settings */ port->dma_ctl_val = 0x11; /* Enable RISC controller and Fifo */ port->ts_int_msk_val = 0x1111; /* TS port bits for RISC */ + port->vld_misc_val = 0x0; + port->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4); spin_lock_init(&port->slock); port->dev = dev; @@ -545,7 +547,7 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *p port->reg_ts_clk_en = VID_B_TS_CLK_EN; port->reg_src_sel = VID_B_SRC_SEL; port->reg_ts_int_msk = VID_B_INT_MSK; - port->reg_ts_int_stat = VID_B_INT_STAT; + port->reg_ts_int_stat = VID_B_INT_STAT; port->sram_chno = SRAM_CH03; /* VID_B */ port->pci_irqmask = 0x02; /* VID_B bit1 */ break; @@ -698,10 +700,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->i2c_bus[2].reg_wdata = I2C3_WDATA; dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */ - if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) + if ((cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) || + (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)) cx23885_init_tsport(dev, &dev->ts1, 1); - if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) + if ((cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) || + (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) cx23885_init_tsport(dev, &dev->ts2, 2); if (get_resources(dev) < 0) { @@ -745,8 +749,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[0]); cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); - cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); cx23885_card_setup(dev); + cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { @@ -761,11 +765,26 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n", __func__); } + } else + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { + if (cx23885_417_register(dev) < 0) { + printk(KERN_ERR + "%s() Failed to register 417 on VID_B\n", + __func__); + } } if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) { if (cx23885_dvb_register(&dev->ts2) < 0) { - printk(KERN_ERR "%s() Failed to register dvb adapters on VID_C\n", + printk(KERN_ERR + "%s() Failed to register dvb on VID_C\n", + __func__); + } + } else + if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) { + if (cx23885_417_register(dev) < 0) { + printk(KERN_ERR + "%s() Failed to register 417 on VID_C\n", __func__); } } @@ -786,12 +805,18 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) cx23885_video_unregister(dev); - if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) + if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) cx23885_dvb_unregister(&dev->ts1); - if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_417_unregister(dev); + + if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) cx23885_dvb_unregister(&dev->ts2); + if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) + cx23885_417_unregister(dev); + cx23885_i2c_unregister(&dev->i2c_bus[2]); cx23885_i2c_unregister(&dev->i2c_bus[1]); cx23885_i2c_unregister(&dev->i2c_bus[0]); @@ -1013,6 +1038,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, struct cx23885_buffer *buf) { struct cx23885_dev *dev = port->dev; + u32 reg; dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, buf->vb.width, buf->vb.height, buf->vb.field); @@ -1038,15 +1064,18 @@ static int cx23885_start_dma(struct cx23885_tsport *port, return -EINVAL; } + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_av_clk(dev, 0); + udelay(100); /* If the port supports SRC SELECT, configure it */ if(port->reg_src_sel) cx_write(port->reg_src_sel, port->src_sel_val); - cx_write(port->reg_hw_sop_ctrl, 0x47 << 16 | 188 << 4); + cx_write(port->reg_hw_sop_ctrl, port->hw_sop_ctrl_val); cx_write(port->reg_ts_clk_en, port->ts_clk_en_val); - cx_write(port->reg_vld_misc, 0x00); + cx_write(port->reg_vld_misc, port->vld_misc_val); cx_write(port->reg_gen_ctrl, port->gen_ctrl_val); udelay(100); @@ -1055,6 +1084,21 @@ static int cx23885_start_dma(struct cx23885_tsport *port, cx_write(port->reg_gpcnt_ctl, 3); q->count = 1; + if (cx23885_boards[dev->board].portb & CX23885_MPEG_ENCODER) { + + reg = cx_read(PAD_CTRL); + reg = reg & ~0x1; /* Clear TS1_OE */ + + /* FIXME, bit 2 writing here is questionable */ + /* set TS1_SOP_OE and TS1_OE_HI */ + reg = reg | 0xa; + cx_write(PAD_CTRL, reg); + + /* FIXME and these two registers should be documented. */ + cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011); + cx_write(ALT_PIN_OUT_SEL, 0x10100045); + } + switch(dev->bridge) { case CX23885_BRIDGE_885: case CX23885_BRIDGE_887: @@ -1070,6 +1114,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port, cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */ + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_av_clk(dev, 1); + if (debug > 4) cx23885_tsport_reg_dump(port); @@ -1079,12 +1126,38 @@ static int cx23885_start_dma(struct cx23885_tsport *port, static int cx23885_stop_dma(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; + u32 reg; + dprintk(1, "%s()\n", __func__); /* Stop interrupts and DMA */ cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val); cx_clear(port->reg_dma_ctl, port->dma_ctl_val); + if (cx23885_boards[dev->board].portb & CX23885_MPEG_ENCODER) { + + reg = cx_read(PAD_CTRL); + + /* Set TS1_OE */ + reg = reg | 0x1; + + /* clear TS1_SOP_OE and TS1_OE_HI */ + reg = reg & ~0xa; + cx_write(PAD_CTRL, reg); +#if 0 + /* + * cx_write(CLK_DELAY, cx_read(CLK_DELAY) & ~0x80000011); ???? + * cx_write(ALT_PIN_OUT_SEL, 0x10100045); ?? need to undo this? + */ +#endif + cx_write(port->reg_src_sel, 0); + cx_write(port->reg_gen_ctrl, 8); + + } + + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_av_clk(dev, 0); + return 0; } @@ -1240,7 +1313,6 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, spin_unlock_irqrestore(&port->slock, flags); } -#if 0 void cx23885_cancel_buffers(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -1251,7 +1323,6 @@ void cx23885_cancel_buffers(struct cx23885_tsport *port) cx23885_stop_dma(port); do_cancel_buffers(port, "cancel", 0); } -#endif /* 0 */ static void cx23885_timeout(unsigned long data) { @@ -1267,16 +1338,77 @@ static void cx23885_timeout(unsigned long data) do_cancel_buffers(port, "timeout", 1); } +int cx23885_irq_417(struct cx23885_dev *dev, u32 status) +{ + /* FIXME: port1 assumption here. */ + struct cx23885_tsport *port = &dev->ts1; + int count = 0; + int handled = 0; + + if (status == 0) + return handled; + + count = cx_read(port->reg_gpcnt); + dprintk(7, "status: 0x%08x mask: 0x%08x count: 0x%x\n", + status, cx_read(port->reg_ts_int_msk), count); + + if ((status & VID_B_MSK_BAD_PKT) || + (status & VID_B_MSK_OPC_ERR) || + (status & VID_B_MSK_VBI_OPC_ERR) || + (status & VID_B_MSK_SYNC) || + (status & VID_B_MSK_VBI_SYNC) || + (status & VID_B_MSK_OF) || + (status & VID_B_MSK_VBI_OF)) { + printk(KERN_ERR "%s: V4L mpeg risc op code error, status " + "= 0x%x\n", dev->name, status); + if (status & VID_B_MSK_BAD_PKT) + dprintk(1, " VID_B_MSK_BAD_PKT\n"); + if (status & VID_B_MSK_OPC_ERR) + dprintk(1, " VID_B_MSK_OPC_ERR\n"); + if (status & VID_B_MSK_VBI_OPC_ERR) + dprintk(1, " VID_B_MSK_VBI_OPC_ERR\n"); + if (status & VID_B_MSK_SYNC) + dprintk(1, " VID_B_MSK_SYNC\n"); + if (status & VID_B_MSK_VBI_SYNC) + dprintk(1, " VID_B_MSK_VBI_SYNC\n"); + if (status & VID_B_MSK_OF) + dprintk(1, " VID_B_MSK_OF\n"); + if (status & VID_B_MSK_VBI_OF) + dprintk(1, " VID_B_MSK_VBI_OF\n"); + + cx_clear(port->reg_dma_ctl, port->dma_ctl_val); + cx23885_sram_channel_dump(dev, + &dev->sram_channels[port->sram_chno]); + cx23885_417_check_encoder(dev); + } else if (status & VID_B_MSK_RISCI1) { + dprintk(7, " VID_B_MSK_RISCI1\n"); + spin_lock(&port->slock); + cx23885_wakeup(port, &port->mpegq, count); + spin_unlock(&port->slock); + } else if (status & VID_B_MSK_RISCI2) { + dprintk(7, " VID_B_MSK_RISCI2\n"); + spin_lock(&port->slock); + cx23885_restart_queue(port, &port->mpegq); + spin_unlock(&port->slock); + } + if (status) { + cx_write(port->reg_ts_int_stat, status); + handled = 1; + } + + return handled; +} + static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) { struct cx23885_dev *dev = port->dev; int handled = 0; u32 count; - if ( (status & VID_BC_MSK_OPC_ERR) || - (status & VID_BC_MSK_BAD_PKT) || - (status & VID_BC_MSK_SYNC) || - (status & VID_BC_MSK_OF)) + if ((status & VID_BC_MSK_OPC_ERR) || + (status & VID_BC_MSK_BAD_PKT) || + (status & VID_BC_MSK_SYNC) || + (status & VID_BC_MSK_OF)) { if (status & VID_BC_MSK_OPC_ERR) dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n", VID_BC_MSK_OPC_ERR); @@ -1290,7 +1422,8 @@ static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) printk(KERN_ERR "%s: mpeg risc op code error\n", dev->name); cx_clear(port->reg_dma_ctl, port->dma_ctl_val); - cx23885_sram_channel_dump(dev, &dev->sram_channels[ port->sram_chno ]); + cx23885_sram_channel_dump(dev, + &dev->sram_channels[port->sram_chno]); } else if (status & VID_BC_MSK_RISCI1) { @@ -1395,11 +1528,17 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (ts1_status) { if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) handled += cx23885_irq_ts(ts1, ts1_status); + else + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + handled += cx23885_irq_417(dev, ts1_status); } if (ts2_status) { if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) handled += cx23885_irq_ts(ts2, ts2_status); + else + if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) + handled += cx23885_irq_417(dev, ts2_status); } if (vida_status) @@ -1439,7 +1578,8 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, - dev->pci_lat, (unsigned long long)pci_resource_start(pci_dev,0)); + dev->pci_lat, + (unsigned long long)pci_resource_start(pci_dev, 0)); pci_set_master(pci_dev); if (!pci_dma_supported(pci_dev, 0xffffffff)) { diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 689c54541..12f3ebdde 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -37,10 +37,11 @@ #include "tda18271.h" #include "lgdt330x.h" #include "xc5000.h" -#include "dvb-pll.h" +#include "tda10048.h" #include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" #include "tuner-simple.h" +#include "dib7000p.h" +#include "dibx000_common.h" static unsigned int debug; @@ -108,6 +109,13 @@ static struct s5h1409_config hauppauge_generic_config = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct tda10048_config hauppauge_hvr1200_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON +}; + static struct s5h1409_config hauppauge_ezqam_config = { .demod_address = 0x32 >> 1, .output_mode = S5H1409_SERIAL_OUTPUT, @@ -179,6 +187,96 @@ static struct tda18271_config hauppauge_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda18271_config hauppauge_hvr1200_tuner_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +static struct dibx000_agc_config xc3028_agc_config = { + BAND_VHF | BAND_UHF, /* band_caps */ + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, + * P_agc_nb_est=2, P_agc_write=0 + */ + (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | + (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ + + 712, /* inv_gain */ + 21, /* time_stabiliz */ + + 0, /* alpha_level */ + 118, /* thlock */ + + 0, /* wbd_inv */ + 2867, /* wbd_ref */ + 0, /* wbd_sel */ + 2, /* wbd_alpha */ + + 0, /* agc1_max */ + 0, /* agc1_min */ + 39718, /* agc2_max */ + 9930, /* agc2_min */ + 0, /* agc1_pt1 */ + 0, /* agc1_pt2 */ + 0, /* agc1_pt3 */ + 0, /* agc1_slope1 */ + 0, /* agc1_slope2 */ + 0, /* agc2_pt1 */ + 128, /* agc2_pt2 */ + 29, /* agc2_slope1 */ + 29, /* agc2_slope2 */ + + 17, /* alpha_mant */ + 27, /* alpha_exp */ + 23, /* beta_mant */ + 51, /* beta_exp */ + + 1, /* perform_agc_softsplit */ +}; + +/* PLL Configuration for COFDM BW_MHz = 8.000000 + * With external clock = 30.000000 */ +static struct dibx000_bandwidth_config xc3028_bw_config = { + 60000, /* internal */ + 30000, /* sampling */ + 1, /* pll_cfg: prediv */ + 8, /* pll_cfg: ratio */ + 3, /* pll_cfg: range */ + 1, /* pll_cfg: reset */ + 0, /* pll_cfg: bypass */ + 0, /* misc: refdiv */ + 0, /* misc: bypclk_div */ + 1, /* misc: IO_CLK_en_core */ + 1, /* misc: ADClkSrc */ + 0, /* misc: modulo */ + (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ + (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ + 20452225, /* timf */ + 30000000 /* xtal_hz */ +}; + +static struct dib7000p_config hauppauge_hvr1400_dib7000_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 0, + .update_lna = NULL, + + .agc_config_count = 1, + .agc = &xc3028_agc_config, + .bw = &xc3028_bw_config, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + .agc_control = NULL, + .spur_protect = 0, + + .output_mode = OUTMODE_MPEG2_SERIAL, +}; + static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) { struct cx23885_tsport *port = ptr; @@ -287,12 +385,10 @@ static int dvb_register(struct cx23885_tsport *port) port->dvb.frontend = dvb_attach(s5h1409_attach, &hauppauge_hvr1500q_config, &dev->i2c_bus[0].i2c_adap); - if (port->dvb.frontend != NULL) { - hauppauge_hvr1500q_tunerconfig.priv = i2c_bus; + if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, &i2c_bus->i2c_adap, - &hauppauge_hvr1500q_tunerconfig); - } + &hauppauge_hvr1500q_tunerconfig, i2c_bus); break; case CX23885_BOARD_HAUPPAUGE_HVR1500: i2c_bus = &dev->i2c_bus[1]; @@ -309,7 +405,47 @@ static int dvb_register(struct cx23885_tsport *port) static struct xc2028_ctrl ctl = { .fname = "xc3028-v27.fw", .max_len = 64, - .scode_table = OREN538, + .scode_table = XC3028_FE_OREN538, + }; + + fe = dvb_attach(xc2028_attach, + port->dvb.frontend, &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + i2c_bus = &dev->i2c_bus[0]; + port->dvb.frontend = dvb_attach(tda10048_attach, + &hauppauge_hvr1200_config, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, port->dvb.frontend, + &dev->i2c_bus[1].i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, port->dvb.frontend, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_hvr1200_tuner_config); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1400: + i2c_bus = &dev->i2c_bus[0]; + port->dvb.frontend = dvb_attach(dib7000p_attach, + &i2c_bus->i2c_adap, + 0x12, &hauppauge_hvr1400_dib7000_config); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_bus[1].i2c_adap, + .i2c_addr = 0x64, + .callback = cx23885_hvr1500_xc3028_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = "xc3028L-v36.fw", + .max_len = 64, + .demod = 5000, + .d2633 = 1 }; fe = dvb_attach(xc2028_attach, diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index a06d50396..ba8e27ca9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -374,6 +374,8 @@ static struct i2c_client cx23885_i2c_client_template = { }; static char *i2c_devs[128] = { + [0x10 >> 1] = "tda10048", + [0x12 >> 1] = "dib7000pc", [ 0x1c >> 1 ] = "lgdt3303", [ 0x86 >> 1 ] = "tda9887", [ 0x32 >> 1 ] = "cx24227", @@ -381,7 +383,8 @@ static char *i2c_devs[128] = { [ 0x84 >> 1 ] = "tda8295", [ 0xa0 >> 1 ] = "eeprom", [ 0xc0 >> 1 ] = "tuner/mt2131/tda8275", - [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275/xc5000", + [0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028", + [0xc8 >> 1] = "tuner/xc3028L", }; static void do_i2c_scan(char *name, struct i2c_client *c) @@ -441,6 +444,29 @@ int cx23885_i2c_unregister(struct cx23885_i2c *bus) return 0; } +void cx23885_av_clk(struct cx23885_dev *dev, int enable) +{ + /* write 0 to bus 2 addr 0x144 via i2x_xfer() */ + char buffer[3]; + struct i2c_msg msg; + dprintk(1, "%s(enabled = %d)\n", __func__, enable); + + /* Register 0x144 */ + buffer[0] = 0x01; + buffer[1] = 0x44; + if (enable == 1) + buffer[2] = 0x05; + else + buffer[2] = 0x00; + + msg.addr = 0x44; + msg.flags = I2C_M_TEN; + msg.len = 3; + msg.buf = buffer; + + i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); +} + /* ----------------------------------------------------------------------- */ /* diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index d66fe2484..4d65080d3 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -33,6 +33,7 @@ #include "btcx-risc.h" #include "cx23885-reg.h" +#include "media/cx2341x.h" #include <linux/version.h> #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) @@ -62,6 +63,9 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4 #define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5 #define CX23885_BOARD_HAUPPAUGE_HVR1500 6 +#define CX23885_BOARD_HAUPPAUGE_HVR1200 7 +#define CX23885_BOARD_HAUPPAUGE_HVR1700 8 +#define CX23885_BOARD_HAUPPAUGE_HVR1400 9 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -157,6 +161,7 @@ typedef enum { CX23885_MPEG_UNDEFINED = 0, CX23885_MPEG_DVB, CX23885_ANALOG_VIDEO, + CX23885_MPEG_ENCODER, } port_t; struct cx23885_board { @@ -255,6 +260,8 @@ struct cx23885_tsport { u32 gen_ctrl_val; u32 ts_clk_en_val; u32 src_sel_val; + u32 vld_misc_val; + u32 hw_sop_ctrl_val; }; struct cx23885_dev { @@ -319,6 +326,14 @@ struct cx23885_dev { struct cx23885_dmaqueue vidq; struct cx23885_dmaqueue vbiq; spinlock_t slock; + + /* MPEG Encoder ONLY settings */ + u32 cx23417_mailbox; + struct cx2341x_mpeg_params mpeg_params; + struct video_device *v4l_device; + atomic_t v4l_reader_count; + struct cx23885_tvnorm encodernorm; + }; extern struct list_head cx23885_devlist; @@ -438,6 +453,18 @@ extern int cx23885_i2c_register(struct cx23885_i2c *bus); extern int cx23885_i2c_unregister(struct cx23885_i2c *bus); extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd, void *arg); +extern void cx23885_av_clk(struct cx23885_dev *dev, int enable); + +/* ----------------------------------------------------------- */ +/* cx23885-417.c */ +extern int cx23885_417_register(struct cx23885_dev *dev); +extern void cx23885_417_unregister(struct cx23885_dev *dev); +extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status); +extern void cx23885_417_check_encoder(struct cx23885_dev *dev); +extern void cx23885_mc417_init(struct cx23885_dev *dev); +extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); +extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); + /* ----------------------------------------------------------- */ /* tv norms */ diff --git a/linux/drivers/media/video/cx25840/Kconfig b/linux/drivers/media/video/cx25840/Kconfig index 7cf29a03e..448f4cd0c 100644 --- a/linux/drivers/media/video/cx25840/Kconfig +++ b/linux/drivers/media/video/cx25840/Kconfig @@ -1,6 +1,7 @@ config VIDEO_CX25840 tristate "Conexant CX2584x audio/video decoders" depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on HOTPLUG # due to FW_LOADER select FW_LOADER ---help--- Support for the Conexant CX2584x audio/video decoders. diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 82e7fb267..e4a25d7cc 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -1238,7 +1238,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, /* ----------------------------------------------------------------------- */ -static int cx25840_probe(struct i2c_client *client) +static int cx25840_probe(struct i2c_client *client, + const struct i2c_device_id *did) { struct cx25840_state *state; u32 id; @@ -1297,6 +1298,12 @@ static int cx25840_probe(struct i2c_client *client) state->id = id; state->rev = device_id; + if (state->is_cx23885) { + /* Drive GPIO2 direction and values */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + } + return 0; } diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index bcf6d9ba0..10e20d819 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -2,7 +2,6 @@ config VIDEO_CX88 tristate "Conexant 2388x (bt878 successor) support" depends on VIDEO_DEV && PCI && I2C && INPUT select I2C_ALGOBIT - select FW_LOADER select VIDEO_BTCX select VIDEOBUF_DMA_SG select VIDEO_TUNER @@ -34,8 +33,9 @@ config VIDEO_CX88_ALSA config VIDEO_CX88_BLACKBIRD tristate "Blackbird MPEG encoder support (cx2388x + cx23416)" - depends on VIDEO_CX88 + depends on VIDEO_CX88 && HOTPLUG select VIDEO_CX2341X + select FW_LOADER ---help--- This adds support for MPEG encoder cards based on the Blackbird reference design, using the Conexant 2388x @@ -57,7 +57,8 @@ config VIDEO_CX88_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/linux/drivers/media/video/cx88/Makefile b/linux/drivers/media/video/cx88/Makefile index 532cee35e..6ec30f242 100644 --- a/linux/drivers/media/video/cx88/Makefile +++ b/linux/drivers/media/video/cx88/Makefile @@ -10,5 +10,6 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.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 diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 5bf2fd5bc..9e87bcb98 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -755,10 +755,8 @@ MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); static int snd_cx88_free(snd_cx88_card_t *chip) { - if (chip->irq >= 0){ - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } cx88_core_put(chip->core,chip->pci); diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index a77616c3f..0a258bfb5 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -556,10 +556,12 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) if (retval < 0) return retval; - dev->mailbox = blackbird_find_mailbox(dev); - if (dev->mailbox < 0) + retval = blackbird_find_mailbox(dev); + if (retval < 0) return -1; + dev->mailbox = retval; + retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ if (retval < 0) { dprintk(0, "ERROR: Firmware ping failed!\n"); diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index fcecfeed9..fdcfb39ef 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -71,6 +71,9 @@ MODULE_PARM_DESC(latency,"pci latency timer"); /* ------------------------------------------------------------------ */ /* board config info */ +/* If radio_type !=UNSET, radio_addr should be specified + */ + static const struct cx88_board cx88_boards[] = { [CX88_BOARD_UNKNOWN] = { .name = "UNKNOWN/GENERIC", @@ -1410,6 +1413,10 @@ static const struct cx88_board cx88_boards[] = { }}, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0xe780, + }, }, [CX88_BOARD_ADSTECH_PTV_390] = { .name = "ADS Tech Instant Video PCI", @@ -1637,6 +1644,7 @@ static const struct cx88_board cx88_boards[] = { .vmux = 2, .gpio0 = 0x16d9, }}, + .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PROLINK_PV_8000GT] = { .name = "Prolink Pixelview MPEG 8000GT", @@ -2491,25 +2499,31 @@ EXPORT_SYMBOL_GPL(cx88_setup_xc3028); static void cx88_card_setup(struct cx88_core *core) { static u8 eeprom[256]; + struct tuner_setup tun_setup; + unsigned int mode_mask = T_RADIO | + T_ANALOG_TV | + T_DIGITAL_TV; + + memset(&tun_setup, 0, sizeof(tun_setup)); if (0 == core->i2c_rc) { core->i2c_client.addr = 0xa0 >> 1; - tveeprom_read(&core->i2c_client,eeprom,sizeof(eeprom)); + tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom)); } switch (core->boardnr) { case CX88_BOARD_HAUPPAUGE: case CX88_BOARD_HAUPPAUGE_ROSLYN: if (0 == core->i2c_rc) - hauppauge_eeprom(core,eeprom+8); + hauppauge_eeprom(core, eeprom+8); break; case CX88_BOARD_GDI: if (0 == core->i2c_rc) - gdi_eeprom(core,eeprom); + gdi_eeprom(core, eeprom); break; case CX88_BOARD_WINFAST2000XP_EXPERT: if (0 == core->i2c_rc) - leadtek_eeprom(core,eeprom); + leadtek_eeprom(core, eeprom); break; case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: @@ -2519,7 +2533,7 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_HVR3000: case CX88_BOARD_HAUPPAUGE_HVR1300: if (0 == core->i2c_rc) - hauppauge_eeprom(core,eeprom); + hauppauge_eeprom(core, eeprom); break; case CX88_BOARD_KWORLD_DVBS_100: cx_write(MO_GP0_IO, 0x000007f8); @@ -2600,6 +2614,35 @@ static void cx88_card_setup(struct cx88_core *core) cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg); } + } /*end switch() */ + + + /* Setup tuners */ + if ((core->board.radio_type != UNSET)) { + tun_setup.mode_mask = T_RADIO; + tun_setup.type = core->board.radio_type; + tun_setup.addr = core->board.radio_addr; + tun_setup.tuner_callback = cx88_tuner_callback; + cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup); + mode_mask &= ~T_RADIO; + } + + if (core->board.tuner_type != TUNER_ABSENT) { + tun_setup.mode_mask = mode_mask; + tun_setup.type = core->board.tuner_type; + tun_setup.addr = core->board.tuner_addr; + tun_setup.tuner_callback = cx88_tuner_callback; + + cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup); + } + + if (core->board.tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &core->board.tda9887_conf; + + cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tda9887_cfg); } if (core->board.tuner_type == TUNER_XC2028) { @@ -2617,6 +2660,7 @@ static void cx88_card_setup(struct cx88_core *core) ctl.fname); cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg); } + cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL); } /* ------------------------------------------------------------------ */ @@ -2755,7 +2799,6 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) if (TUNER_ABSENT != core->board.tuner_type) request_module("tuner"); - cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL); cx88_card_setup(core); cx88_ir_init(core, pci); diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 8559ef873..fc5807aeb 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -46,9 +46,9 @@ #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" -#include "tuner-xc2028-types.h" #include "tuner-simple.h" #include "tda9887.h" +#include "s5h1411.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); @@ -471,6 +471,22 @@ static struct zl10353_config cx88_geniatech_x8000_mt = { #endif }; +static struct s5h1411_config dvico_fusionhdtv7_config = { + .output_mode = S5H1411_SERIAL_OUTPUT, + .gpio = S5H1411_GPIO_ON, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .qam_if = S5H1411_IF_44000, + .vsb_if = S5H1411_IF_44000, + .inversion = S5H1411_INVERSION_OFF, + .status_mode = S5H1411_DEMODLOCKING +}; + +static struct xc5000_config dvico_fusionhdtv7_tuner_config = { + .i2c_address = 0xc2 >> 1, + .if_khz = 5380, + .tuner_callback = cx88_tuner_callback, +}; + static int attach_xc3028(u8 addr, struct cx8802_dev *dev) { struct dvb_frontend *fe; @@ -500,9 +516,6 @@ static int attach_xc3028(u8 addr, struct cx8802_dev *dev) if (!fe) { printk(KERN_ERR "%s/2: xc3028 attach failed\n", dev->core->name); - dvb_frontend_detach(dev->dvb.frontend); - dvb_unregister_frontend(dev->dvb.frontend); - dev->dvb.frontend = NULL; return -EINVAL; } @@ -514,20 +527,23 @@ static int attach_xc3028(u8 addr, struct cx8802_dev *dev) static int dvb_register(struct cx8802_dev *dev) { + struct cx88_core *core = dev->core; + /* init struct videobuf_dvb */ - dev->dvb.name = dev->core->name; + dev->dvb.name = core->name; dev->ts_gen_cntrl = 0x0c; /* init frontend */ - switch (dev->core->boardnr) { + switch (core->boardnr) { case CX88_BOARD_HAUPPAUGE_DVB_T1: dev->dvb.frontend = dvb_attach(cx22702_attach, &connexant_refboard_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - DVB_PLL_THOMSON_DTT759X); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x61, &core->i2c_adap, + DVB_PLL_THOMSON_DTT759X)) + goto frontend_detach; } break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: @@ -536,11 +552,12 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_WINFAST_DTV1000: dev->dvb.frontend = dvb_attach(cx22702_attach, &connexant_refboard_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, - &dev->core->i2c_adap, - DVB_PLL_THOMSON_DTT7579); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x60, &core->i2c_adap, + DVB_PLL_THOMSON_DTT7579)) + goto frontend_detach; } break; case CX88_BOARD_WINFAST_DTV2000H: @@ -550,29 +567,32 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_HAUPPAUGE_HVR3000: dev->dvb.frontend = dvb_attach(cx22702_attach, &hauppauge_hvr_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3); + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, - NULL, DVB_PLL_THOMSON_DTT7579); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x60, NULL, DVB_PLL_THOMSON_DTT7579)) + goto frontend_detach; break; } /* ZL10353 replaces MT352 on later cards */ dev->dvb.frontend = dvb_attach(zl10353_attach, &dvico_fusionhdtv_plus_v1_1, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, - NULL, DVB_PLL_THOMSON_DTT7579); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x60, NULL, DVB_PLL_THOMSON_DTT7579)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: @@ -580,28 +600,31 @@ static int dvb_register(struct cx8802_dev *dev) * compatible, with a slightly different MT352 AGC gain. */ dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv_dual, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_THOMSON_DTT7579); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x61, NULL, DVB_PLL_THOMSON_DTT7579)) + goto frontend_detach; break; } /* ZL10353 replaces MT352 on later cards */ dev->dvb.frontend = dvb_attach(zl10353_attach, &dvico_fusionhdtv_plus_v1_1, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_THOMSON_DTT7579); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x61, NULL, DVB_PLL_THOMSON_DTT7579)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_LG_Z201); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x61, NULL, DVB_PLL_LG_Z201)) + goto frontend_detach; } break; case CX88_BOARD_KWORLD_DVB_T: @@ -609,10 +632,11 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_ADSTECH_DVB_T_PCI: dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_UNKNOWN_1); + if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, + 0x61, NULL, DVB_PLL_UNKNOWN_1)) + goto frontend_detach; } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: @@ -621,32 +645,35 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, &dev->vp3054->adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3); + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3)) + goto frontend_detach; } #else - printk(KERN_ERR "%s/2: built without vp3054 support\n", dev->core->name); + printk(KERN_ERR "%s/2: built without vp3054 support\n", + core->name); #endif break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: dev->dvb.frontend = dvb_attach(zl10353_attach, &dvico_fusionhdtv_hybrid, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_THOMSON_FE6600); + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_THOMSON_FE6600)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: dev->dvb.frontend = dvb_attach(zl10353_attach, &dvico_fusionhdtv_xc3028, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend == NULL) dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv_mt352_xc3028, - &dev->core->i2c_adap); + &core->i2c_adap); /* * On this board, the demod provides the I2C bus pullup. * We must not permit gate_ctrl to be performed, or @@ -659,19 +686,18 @@ static int dvb_register(struct cx8802_dev *dev) break; case CX88_BOARD_PCHDTV_HD3000: dev->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_THOMSON_DTT761X); + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_THOMSON_DTT761X)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: dev->ts_gen_cntrl = 0x08; - { - /* Do a hardware reset of chip before using it. */ - struct cx88_core *core = dev->core; + /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); mdelay(100); cx_set(MO_GP0_IO, 1); @@ -681,146 +707,144 @@ static int dvb_register(struct cx8802_dev *dev) fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; dev->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_MICROTUNE_4042FI5); - } + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_MICROTUNE_4042FI5)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: dev->ts_gen_cntrl = 0x08; - { - /* Do a hardware reset of chip before using it. */ - struct cx88_core *core = dev->core; + /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); mdelay(100); cx_set(MO_GP0_IO, 9); mdelay(200); dev->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_THOMSON_DTT761X); - } + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_THOMSON_DTT761X)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: dev->ts_gen_cntrl = 0x08; - { - /* Do a hardware reset of chip before using it. */ - struct cx88_core *core = dev->core; + /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); dev->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_5_gold, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_LG_TDVS_H06XF); - dvb_attach(tda9887_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x43); - } + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF)) + goto frontend_detach; + if (!dvb_attach(tda9887_attach, dev->dvb.frontend, + &core->i2c_adap, 0x43)) + goto frontend_detach; } break; case CX88_BOARD_PCHDTV_HD5500: dev->ts_gen_cntrl = 0x08; - { - /* Do a hardware reset of chip before using it. */ - struct cx88_core *core = dev->core; + /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); dev->dvb.frontend = dvb_attach(lgdt330x_attach, &pchdtv_hd5500, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_LG_TDVS_H06XF); - dvb_attach(tda9887_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x43); - } + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF)) + goto frontend_detach; + if (!dvb_attach(tda9887_attach, dev->dvb.frontend, + &core->i2c_adap, 0x43)) + goto frontend_detach; } break; case CX88_BOARD_ATI_HDTVWONDER: dev->dvb.frontend = dvb_attach(nxt200x_attach, &ati_hdtvwonder, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(simple_tuner_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_TUV1236D); + if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &core->i2c_adap, 0x61, + TUNER_PHILIPS_TUV1236D)) + goto frontend_detach; } break; case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: dev->dvb.frontend = dvb_attach(cx24123_attach, &hauppauge_novas_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend) { - dvb_attach(isl6421_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x08, 0x00, 0x00); + if (!dvb_attach(isl6421_attach, dev->dvb.frontend, + &core->i2c_adap, 0x08, 0x00, 0x00)) + goto frontend_detach; } break; case CX88_BOARD_KWORLD_DVBS_100: dev->dvb.frontend = dvb_attach(cx24123_attach, &kworld_dvbs_100_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend) { - dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; + core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; dev->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage; } break; case CX88_BOARD_GENIATECH_DVBS: dev->dvb.frontend = dvb_attach(cx24123_attach, &geniatech_dvbs_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend) { - dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; + core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: dev->dvb.frontend = dvb_attach(s5h1409_attach, &pinnacle_pctv_hd_800i_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { /* tuner_config.video_dev must point to * i2c_adap.algo_data */ - pinnacle_pctv_hd_800i_tuner_config.priv = - dev->core->i2c_adap.algo_data; - dvb_attach(xc5000_attach, dev->dvb.frontend, - &dev->core->i2c_adap, - &pinnacle_pctv_hd_800i_tuner_config); + if (!dvb_attach(xc5000_attach, dev->dvb.frontend, + &core->i2c_adap, + &pinnacle_pctv_hd_800i_tuner_config, + core->i2c_adap.algo_data)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: dev->dvb.frontend = dvb_attach(s5h1409_attach, &dvico_hdtv5_pci_nano_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { struct dvb_frontend *fe; struct xc2028_config cfg = { - .i2c_adap = &dev->core->i2c_adap, + .i2c_adap = &core->i2c_adap, .i2c_addr = 0x61, .callback = cx88_pci_nano_callback, }; static struct xc2028_ctrl ctl = { .fname = "xc3028-v27.fw", .max_len = 64, - .scode_table = OREN538, + .scode_table = XC3028_FE_OREN538, }; fe = dvb_attach(xc2028_attach, @@ -832,35 +856,50 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_PINNACLE_HYBRID_PCTV: dev->dvb.frontend = dvb_attach(zl10353_attach, &cx88_geniatech_x8000_mt, - &dev->core->i2c_adap); + &core->i2c_adap); if (attach_xc3028(0x61, dev) < 0) - return -EINVAL; + goto frontend_detach; break; case CX88_BOARD_GENIATECH_X8000_MT: dev->ts_gen_cntrl = 0x00; dev->dvb.frontend = dvb_attach(zl10353_attach, &cx88_geniatech_x8000_mt, - &dev->core->i2c_adap); + &core->i2c_adap); if (attach_xc3028(0x61, dev) < 0) - return -EINVAL; + goto frontend_detach; break; case CX88_BOARD_KWORLD_ATSC_120: dev->dvb.frontend = dvb_attach(s5h1409_attach, &kworld_atsc_120_config, - &dev->core->i2c_adap); + &core->i2c_adap); if (attach_xc3028(0x61, dev) < 0) - return -EINVAL; + goto frontend_detach; + break; + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + dev->dvb.frontend = dvb_attach(s5h1411_attach, + &dvico_fusionhdtv7_config, + &core->i2c_adap); + if (dev->dvb.frontend != NULL) { + /* tuner_config.video_dev must point to + * i2c_adap.algo_data + */ + if (!dvb_attach(xc5000_attach, dev->dvb.frontend, + &core->i2c_adap, + &dvico_fusionhdtv7_tuner_config, + core->i2c_adap.algo_data)) + goto frontend_detach; + } break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", - dev->core->name); + core->name); break; } if (NULL == dev->dvb.frontend) { printk(KERN_ERR "%s/2: frontend initialization failed\n", - dev->core->name); + core->name); return -EINVAL; } @@ -868,11 +907,18 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + cx88_call_i2c_clients(core, TUNER_SET_STANDBY, NULL); /* register everything */ return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev, adapter_nr); + +frontend_detach: + if (dev->dvb.frontend) { + dvb_frontend_detach(dev->dvb.frontend); + dev->dvb.frontend = NULL; + } + return -EINVAL; } /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/video/cx88/cx88-i2c.c b/linux/drivers/media/video/cx88/cx88-i2c.c index 44d01352e..800ea0d64 100644 --- a/linux/drivers/media/video/cx88/cx88-i2c.c +++ b/linux/drivers/media/video/cx88/cx88-i2c.c @@ -102,7 +102,6 @@ static int cx8800_bit_getsda(void *data) static int attach_inform(struct i2c_client *client) { - struct tuner_setup tun_setup; struct cx88_core *core = i2c_get_adapdata(client->adapter); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) @@ -112,37 +111,7 @@ static int attach_inform(struct i2c_client *client) dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", client->driver->driver.name, client->addr, client->name); #endif - if (!client->driver->command) - return 0; - - if (core->board.radio_type != UNSET) { - if ((core->board.radio_addr==ADDR_UNSET)||(core->board.radio_addr==client->addr)) { - tun_setup.mode_mask = T_RADIO; - tun_setup.type = core->board.radio_type; - tun_setup.addr = core->board.radio_addr; - tun_setup.tuner_callback = cx88_tuner_callback; - client->driver->command (client, TUNER_SET_TYPE_ADDR, &tun_setup); - } - } - if (core->board.tuner_type != UNSET) { - if ((core->board.tuner_addr==ADDR_UNSET)||(core->board.tuner_addr==client->addr)) { - - tun_setup.mode_mask = T_ANALOG_TV; - tun_setup.type = core->board.tuner_type; - tun_setup.addr = core->board.tuner_addr; - tun_setup.tuner_callback = cx88_tuner_callback; - client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup); - } - } - - if (core->board.tda9887_conf) { - struct v4l2_priv_tun_config tda9887_cfg; - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &core->board.tda9887_conf; - - client->driver->command(client, TUNER_SET_CONFIG, &tda9887_cfg); - } return 0; } diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 0f7a0bd86..16a5af30e 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -1,11 +1,13 @@ config VIDEO_EM28XX - tristate "Empia EM2800/2820/2840 USB video capture support" + tristate "Empia EM28xx USB video capture support" depends on VIDEO_DEV && I2C && INPUT select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR + select VIDEOBUF_VMALLOC select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Empia 28xx based TV cards. @@ -27,3 +29,12 @@ config VIDEO_EM28XX_ALSA To compile this driver as a module, choose M here: the module will be called em28xx-alsa +config VIDEO_EM28XX_DVB + tristate "DVB/ATSC Support for em28xx based TV cards" + depends on VIDEO_EM28XX && DVB_CORE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select VIDEOBUF_DVB + ---help--- + This adds support for DVB cards based on the + Empiatech em28xx chips. diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 092455099..8137a8c94 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -5,8 +5,10 @@ em28xx-alsa-objs := em28xx-audio.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o +obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-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 diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 06a4e4a2b..a9b6a5ecd 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -37,7 +37,6 @@ #include <media/v4l2-common.h> #include "em28xx.h" -#include "tuner-xc2028.h" static int tuner = -1; module_param(tuner, int, 0444); @@ -53,26 +52,6 @@ struct em28xx_hash_table { unsigned int tuner; }; -/* Boards supported by driver */ - -#define EM2800_BOARD_UNKNOWN 0 -#define EM2820_BOARD_UNKNOWN 1 -#define EM2820_BOARD_TERRATEC_CINERGY_250 2 -#define EM2820_BOARD_PINNACLE_USB_2 3 -#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 -#define EM2820_BOARD_MSI_VOX_USB_2 5 -#define EM2800_BOARD_TERRATEC_CINERGY_200 6 -#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 -#define EM2800_BOARD_KWORLD_USB2800 8 -#define EM2820_BOARD_PINNACLE_DVC_90 9 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 -#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 -#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 -#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 -#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 -#define EM2800_BOARD_VGEAR_POCKETTV 15 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 - struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_UNKNOWN] = { .name = "Unknown EM2800 video grabber", @@ -201,6 +180,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_12mhz_i2s = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -215,9 +195,6 @@ struct em28xx_board em28xx_boards[] = { .vmux = TVP5150_SVIDEO, .amux = 1, } }, - - /* gpio's 4, 1, 0 */ - .analog_gpio = 0x003d2d, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", @@ -444,7 +421,13 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, { USB_DEVICE(0x2040, 0x6502), .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, - { USB_DEVICE(0x2040, 0x6513), + { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */ + .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */ + .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */ + .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x651f), /* HCW HVR-850 */ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x0ccd, 0x0042), .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS }, @@ -454,7 +437,36 @@ struct usb_device_id em28xx_id_table [] = { }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); -/* EEPROM hash table for devices with generic USB IDs */ +/* + * Reset sequences for analog/digital modes + */ + +/* Board Hauppauge WinTV HVR 900 analog */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { + {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, + {0x05, 0xff, 0x10, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0x0f, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 tuner_callback */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_tuner_callback[] = { + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* + * EEPROM hash table for devices with generic USB IDs + */ static struct em28xx_hash_table em28xx_eeprom_hash [] = { /* P/N: SA 60002070465 Tuner: TVF7533-MF */ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, @@ -466,79 +478,113 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, }; +int em28xx_tuner_callback(void *ptr, int command, int arg) +{ + int rc = 0; + struct em28xx *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + if (command != XC2028_TUNER_RESET) + return 0; + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_gpio_set(dev, dev->tun_analog_gpio); + else + rc = em28xx_gpio_set(dev, dev->tun_digital_gpio); + + return rc; +} +EXPORT_SYMBOL_GPL(em28xx_tuner_callback); + +static void em28xx_set_model(struct em28xx *dev) +{ + dev->is_em2800 = em28xx_boards[dev->model].is_em2800; + dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; + dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; + dev->decoder = em28xx_boards[dev->model].decoder; + dev->video_inputs = em28xx_boards[dev->model].vchannels; + dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; + dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; + dev->has_dvb = em28xx_boards[dev->model].has_dvb; +} + /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ void em28xx_pre_card_setup(struct em28xx *dev) { + int rc; + + rc = em28xx_read_reg(dev, EM2880_R04_GPO); + if (rc >= 0) + dev->reg_gpo = rc; + + dev->wait_after_write = 5; + rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (rc > 0) { + switch (rc) { + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", rc); + } + } + em28xx_set_model(dev); + /* request some modules */ switch (dev->model) { case EM2880_BOARD_TERRATEC_PRODIGY_XS: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_TERRATEC_HYBRID_XS: - em28xx_write_regs(dev, XCLK_REG, "\x27", 1); - em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1); - em28xx_write_regs(dev, 0x08, "\xff", 1); - em28xx_write_regs(dev, 0x04, "\x00", 1); - msleep(100); - em28xx_write_regs(dev, 0x04, "\x08", 1); - msleep(100); - em28xx_write_regs(dev, 0x08, "\xff", 1); - msleep(50); - em28xx_write_regs(dev, 0x08, "\x2d", 1); + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); msleep(50); - em28xx_write_regs(dev, 0x08, "\x3d", 1); + + /* Sets GPO/GPIO sequences for this device */ + dev->analog_gpio = hauppauge_wintv_hvr_900_analog; + dev->digital_gpio = hauppauge_wintv_hvr_900_digital; + dev->tun_analog_gpio = hauppauge_wintv_hvr_900_tuner_callback; + dev->tun_digital_gpio = hauppauge_wintv_hvr_900_tuner_callback; + break; } + + em28xx_gpio_set(dev, dev->tun_analog_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + + /* Unlock device */ + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); } -static int em28xx_tuner_callback(void *ptr, int command, int arg) +static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) { - int rc = 0; - struct em28xx *dev = ptr; + memset(ctl, 0, sizeof(*ctl)); - if (dev->tuner_type != TUNER_XC2028) - return 0; - - switch (command) { - case XC2028_TUNER_RESET: - { - /* GPIO and initialization codes for analog TV and radio - This code should be complemented for DTV, since reset - codes are different. - */ - - dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); - dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); - - if (dev->analog_gpio) { - char gpio0 = dev->analog_gpio & 0xff; - char gpio1 = (dev->analog_gpio >> 8) & 0xff; - char gpio4 = dev->analog_gpio >> 24; - - if (gpio4) { - dev->em28xx_write_regs(dev, 0x04, &gpio4, 1); - msleep(140); - } - - msleep(6); - dev->em28xx_write_regs(dev, 0x08, &gpio0, 1); - msleep(10); - dev->em28xx_write_regs(dev, 0x08, &gpio1, 1); - msleep(5); - } + ctl->fname = XC2028_DEFAULT_FIRMWARE; + ctl->max_len = 64; + ctl->mts = em28xx_boards[dev->model].mts_firmware; + switch (dev->model) { + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + ctl->demod = XC3028_FE_ZARLINK456; break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + /* FIXME: Better to specify the needed IF */ + ctl->demod = XC3028_FE_DEFAULT; + break; + default: + ctl->demod = XC3028_FE_OREN538; } - } - return rc; } static void em28xx_config_tuner(struct em28xx *dev) { struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; struct tuner_setup tun_setup; struct v4l2_frequency f; @@ -553,11 +599,9 @@ static void em28xx_config_tuner(struct em28xx *dev) em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); if (dev->tuner_type == TUNER_XC2028) { - memset(&ctl, 0, sizeof(ctl)); + struct xc2028_ctrl ctl; - ctl.fname = XC2028_DEFAULT_FIRMWARE; - ctl.max_len = 64; - ctl.mts = em28xx_boards[dev->model].mts_firmware; + em28xx_setup_xc3028(dev, &ctl); xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; @@ -655,19 +699,6 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } - -static void em28xx_set_model(struct em28xx *dev) -{ - dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; - dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; - dev->decoder = em28xx_boards[dev->model].decoder; - dev->video_inputs = em28xx_boards[dev->model].vchannels; - dev->analog_gpio = em28xx_boards[dev->model].analog_gpio; - dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; - dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; -} - /* ----------------------------------------------------------------------- */ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) { diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 97635abb9..1a30e361c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -53,6 +53,12 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); +/* FIXME */ +#define em28xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + /* * em28xx_read_reg_req() * reads data from the usb device specifying bRequest @@ -71,11 +77,11 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, buf, len, HZ); - if (reg_debug){ + if (reg_debug) { printk(ret < 0 ? " failed!\n" : "%02x values: ", ret); - for (byte = 0; byte < len; byte++) { + for (byte = 0; byte < len; byte++) printk(" %02x", (unsigned char)buf[byte]); - } + printk("\n"); } @@ -128,7 +134,10 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, unsigned char *bufs; if (dev->state & DEV_DISCONNECTED) - return(-ENODEV); + return -ENODEV; + + if (len < 1) + return -EINVAL; bufs = kmalloc(len, GFP_KERNEL); @@ -137,8 +146,8 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if (reg_debug) { int i; for (i = 0; i < len; ++i) - printk (" %02x", (unsigned char)buf[i]); - printk ("\n"); + printk(" %02x", (unsigned char)buf[i]); + printk("\n"); } if (!bufs) @@ -147,14 +156,32 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, bufs, len, HZ); - msleep(5); /* FIXME: magic number */ + if (dev->wait_after_write) + msleep(dev->wait_after_write); + kfree(bufs); return ret; } int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) { - return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + int rc; + + rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + + /* Stores GPO/GPIO values at the cache, if changed + Only write values should be stored, since input on a GPIO + register will return the input bits. + Not sure what happens on reading GPO register. + */ + if (rc >= 0) { + if (reg == EM2880_R04_GPO) + dev->reg_gpo = buf[0]; + else if (reg == EM28XX_R08_GPIO) + dev->reg_gpio = buf[0]; + } + + return rc; } /* @@ -167,9 +194,20 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, { int oldval; u8 newval; - if ((oldval = em28xx_read_reg(dev, reg)) < 0) + + /* Uses cache for gpo/gpio registers */ + if (reg == EM2880_R04_GPO) + oldval = dev->reg_gpo; + else if (reg == EM28XX_R08_GPIO) + oldval = dev->reg_gpio; + else + oldval = em28xx_read_reg(dev, reg); + + if (oldval < 0) return oldval; + newval = (((u8) oldval) & ~bitmask) | (val & bitmask); + return em28xx_write_regs(dev, reg, &newval, 1); } @@ -181,20 +219,26 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) { int ret, i; u8 addr = reg & 0x7f; - if ((ret = em28xx_write_regs(dev, AC97LSB_REG, val, 2)) < 0) + + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2); + if (ret < 0) return ret; - if ((ret = em28xx_write_regs(dev, AC97ADDR_REG, &addr, 1)) < 0) + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) return ret; /* Wait up to 50 ms for AC97 command to complete */ for (i = 0; i < 10; i++) { - if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0) + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) return ret; + if (!(ret & 0x01)) return 0; msleep(5); } - em28xx_warn ("AC97 command still being executed: not handled properly!\n"); + em28xx_warn("AC97 command still being executed: not handled properly!\n"); return 0; } @@ -212,7 +256,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) else input = EM2800_AUDIO_SRC_TUNER; - ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1); + ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); if (ret < 0) return ret; } @@ -238,7 +282,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) } } - ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); + ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); if (ret < 0) return ret; msleep(5); @@ -246,11 +290,11 @@ static int em28xx_set_audio_source(struct em28xx *dev) /* Sets AC97 mixer registers This is seems to be needed, even for non-ac97 configs */ - ret = em28xx_write_ac97(dev, VIDEO_AC97, video); + ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video); if (ret < 0) return ret; - ret = em28xx_write_ac97(dev, LINE_IN_AC97, line); + ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line); return ret; } @@ -266,7 +310,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Mute */ s[1] |= 0x80; - ret = em28xx_write_ac97(dev, MASTER_AC97, s); + ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); if (ret < 0) return ret; @@ -277,7 +321,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (!dev->mute) xclk |= 0x80; - ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7); + ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7); if (ret < 0) return ret; msleep(10); @@ -288,7 +332,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Unmute device */ if (!dev->mute) s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, MASTER_AC97, s); + ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); return ret; } @@ -296,50 +340,68 @@ EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_colorlevels_set_default(struct em28xx *dev) { - em28xx_write_regs(dev, YGAIN_REG, "\x10", 1); /* contrast */ - em28xx_write_regs(dev, YOFFSET_REG, "\x00", 1); /* brightness */ - em28xx_write_regs(dev, UVGAIN_REG, "\x10", 1); /* saturation */ - em28xx_write_regs(dev, UOFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, VOFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, SHARPNESS_REG, "\x00", 1); - - em28xx_write_regs(dev, GAMMA_REG, "\x20", 1); - em28xx_write_regs(dev, RGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, GGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, BGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, ROFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, GOFFSET_REG, "\x00", 1); - return em28xx_write_regs(dev, BOFFSET_REG, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */ + em28xx_write_regs(dev, EM28XX_R21_YOFFSET, "\x00", 1); /* brightness */ + em28xx_write_regs(dev, EM28XX_R22_UVGAIN, "\x10", 1); /* saturation */ + em28xx_write_regs(dev, EM28XX_R23_UOFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R24_VOFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R25_SHARPNESS, "\x00", 1); + + em28xx_write_regs(dev, EM28XX_R14_GAMMA, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R15_RGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R16_GGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R17_BGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R18_ROFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R19_GOFFSET, "\x00", 1); + return em28xx_write_regs(dev, EM28XX_R1A_BOFFSET, "\x00", 1); } int em28xx_capture_start(struct em28xx *dev, int start) { - int ret; + int rc; /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ - if ((ret = em28xx_write_reg_bits(dev, USBSUSP_REG, start ? 0x10 : 0x00, - 0x10)) < 0) - return ret; + rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, + start ? 0x10 : 0x00, 0x10); + if (rc < 0) + return rc; + + if (!start) { + /* disable video capture */ + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1); + return rc; + } + /* enable video capture */ - return em28xx_write_regs(dev, VINENABLE_REG, start ? "\x67" : "\x27", 1); + rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1); + else + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1); + + msleep(6); + + return rc; } int em28xx_outfmt_set_yuv422(struct em28xx *dev) { - em28xx_write_regs(dev, OUTFMT_REG, "\x34", 1); - em28xx_write_regs(dev, VINMODE_REG, "\x10", 1); - return em28xx_write_regs(dev, VINCTRL_REG, "\x11", 1); + em28xx_write_regs(dev, EM28XX_R27_OUTFMT, "\x34", 1); + em28xx_write_regs(dev, EM28XX_R10_VINMODE, "\x10", 1); + return em28xx_write_regs(dev, EM28XX_R11_VINCTRL, "\x11", 1); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, u8 ymin, u8 ymax) { - em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", xmin, ymin, xmax, ymax); + em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", + xmin, ymin, xmax, ymax); - em28xx_write_regs(dev, XMIN_REG, &xmin, 1); - em28xx_write_regs(dev, XMAX_REG, &xmax, 1); - em28xx_write_regs(dev, YMIN_REG, &ymin, 1); - return em28xx_write_regs(dev, YMAX_REG, &ymax, 1); + em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); + em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); + em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); + return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); } static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, @@ -349,34 +411,36 @@ static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u8 cheight = height; u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); - em28xx_coredbg("em28xx Area Set: (%d,%d)\n", (width | (overflow & 2) << 7), + em28xx_coredbg("em28xx Area Set: (%d,%d)\n", + (width | (overflow & 2) << 7), (height | (overflow & 1) << 8)); - em28xx_write_regs(dev, HSTART_REG, &hstart, 1); - em28xx_write_regs(dev, VSTART_REG, &vstart, 1); - em28xx_write_regs(dev, CWIDTH_REG, &cwidth, 1); - em28xx_write_regs(dev, CHEIGHT_REG, &cheight, 1); - return em28xx_write_regs(dev, OFLOW_REG, &overflow, 1); + em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); + em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); + em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); + em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); + return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); } static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if(dev->is_em2800) + if (dev->is_em2800) mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); else { u8 buf[2]; buf[0] = h; buf[1] = h >> 8; - em28xx_write_regs(dev, HSCALELOW_REG, (char *)buf, 2); + em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); buf[0] = v; buf[1] = v >> 8; - em28xx_write_regs(dev, VSCALELOW_REG, (char *)buf, 2); - /* it seems that both H and V scalers must be active to work correctly */ + em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); + /* it seems that both H and V scalers must be active + to work correctly */ mode = (h || v)? 0x30: 0x00; } - return em28xx_write_reg_bits(dev, COMPR_REG, mode, 0x30); + return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); } /* FIXME: this only function read values from dev */ @@ -426,10 +490,246 @@ int em28xx_set_alternate(struct em28xx *dev) dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); if (errCode < 0) { - em28xx_errdev ("cannot change alternate number to %d (error=%i)\n", + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", dev->alt, errCode); return errCode; } } return 0; } + +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) +{ + int rc = 0; + + if (!gpio) + return rc; + + dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + if (dev->mode == EM28XX_ANALOG_MODE) + dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); + else + dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1); + msleep(6); + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + if (gpio->reg >= 0) { + rc = em28xx_write_reg_bits(dev, + gpio->reg, + gpio->val, + gpio->mask); + if (rc < 0) + return rc; + } + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; + } + return rc; +} + +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) +{ + if (dev->mode == set_mode) + return 0; + + if (set_mode == EM28XX_MODE_UNDEFINED) { + dev->mode = set_mode; + return 0; + } + +#if 0 + /* Resource is locked */ + if (dev->mode != EM28XX_MODE_UNDEFINED) + return -EINVAL; +#endif + dev->mode = set_mode; + + if (dev->mode == EM28XX_DIGITAL_MODE) + return em28xx_gpio_set(dev, dev->digital_gpio); + else + return em28xx_gpio_set(dev, dev->analog_gpio); +} +EXPORT_SYMBOL_GPL(em28xx_set_mode); + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void em28xx_irq_callback(struct urb *urb, struct pt_regs *regs) +#else +static void em28xx_irq_callback(struct urb *urb) +#endif +{ + struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + int rc, i; + + /* Copy data from URB */ + spin_lock(&dev->slock); + rc = dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + em28xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void em28xx_uninit_isoc(struct em28xx *dev) +{ + struct urb *urb; + int i; + + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); + + dev->isoc_ctl.nfields = -1; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); + +/* + * Allocate URBs and start IRQ + */ +int em28xx_init_isoc(struct em28xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + em28xx_uninit_isoc(dev); + + dev->isoc_ctl.isoc_copy = isoc_copy; + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + em28xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = max_pkt_size; + dev->isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); + em28xx_uninit_isoc(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + em28xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt()?" while in int":""); + em28xx_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + /* FIXME: this is a hack - should be + 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' + should also be using 'desc.bInterval' + */ + pipe = usb_rcvisocpipe(dev->udev, + dev->mode == EM28XX_ANALOG_MODE ? 0x82 : 0x84); + + usb_fill_int_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + em28xx_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->isoc_ctl.max_pkt_size; + k += dev->isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + em28xx_capture_start(dev, 1); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + em28xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + em28xx_uninit_isoc(dev); + return rc; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_init_isoc); diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c new file mode 100644 index 000000000..a890b8a32 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -0,0 +1,505 @@ +/* + DVB device driver for em28xx + + (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org> + + (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> + - Fixes for the driver to properly work with HVR-950 + + (c) 2008 Aidan Thornton <makosoft@googlemail.com> + + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: + (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> + (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + + 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. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> +#include "compat.h" + +#include "em28xx.h" +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> + +#include "lgdt330x.h" +#include "zl10353.h" + +MODULE_DESCRIPTION("driver for em28xx based DVB cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +#define EM28XX_DVB_NUM_BUFS 5 +#define EM28XX_DVB_MAX_PACKETSIZE 564 +#define EM28XX_DVB_MAX_PACKETS 64 + +struct em28xx_dvb { + struct dvb_frontend *frontend; + + /* feed count management */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) + struct mutex lock; +#else + struct semaphore lock; +#endif + int nfeeds; + + /* general boilerplate stuff */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + struct dvb_adapter adapter; +#else + struct dvb_adapter *adapter; +#endif + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + + +static inline void print_err_status(struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static int start_streaming(struct em28xx_dvb *dvb) +{ + int rc; + struct em28xx *dev = dvb->adapter.priv; + + usb_set_interface(dev->udev, 0, 1); + rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + if (rc < 0) + return rc; + + return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_BUFS, EM28XX_DVB_MAX_PACKETSIZE, + dvb_isoc_copy); +} + +static int stop_streaming(struct em28xx_dvb *dvb) +{ + struct em28xx *dev = dvb->adapter.priv; + + em28xx_uninit_isoc(dev); + + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + + return 0; +} + +static int start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + + + +/* ------------------------------------------------------------------ */ +static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct em28xx *dev = fe->dvb->priv; + + if (acquire) + return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + else + return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); +} + +/* ------------------------------------------------------------------ */ + +static struct lgdt330x_config em2880_lgdt3303_dev = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, +}; + +static struct zl10353_config em28xx_zl10353_with_xc3028 = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45600, +}; + +/* ------------------------------------------------------------------ */ + +static int attach_xc3028(u8 addr, struct em28xx *dev) +{ + struct dvb_frontend *fe; + struct xc2028_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_adap; + cfg.i2c_addr = addr; + cfg.callback = em28xx_tuner_callback; + + if (!dev->dvb->frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc3028\n", + dev->name); + return -EINVAL; + } + + fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc3028 attach failed\n", + dev->name); + dvb_frontend_detach(dev->dvb->frontend); + dev->dvb->frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +int register_dvb(struct em28xx_dvb *dvb, + struct module *module, + struct em28xx *dev, + struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); +#else + dvb->adapter->priv = dev; + + /* register frontend */ + result = dvb_register_frontend(dvb->adapter, dvb->frontend); +#endif + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = start_feed; + dvb->demux.stop_feed = stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); +#else + result = dvb_dmxdev_init(&dvb->dmxdev, dvb->adapter); +#endif + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_fe_conn; + } + + /* register network adapter */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); +#else + dvb_net_init(dvb->adapter, &dvb->net, &dvb->demux.dmx); +#endif + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +fail_adapter: + return result; +} + +static void unregister_dvb(struct em28xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)) + dvb_unregister_adapter(&dvb->adapter); +#else + dvb_unregister_adapter(dvb->adapter); +#endif +} + + +static int dvb_init(struct em28xx *dev) +{ + int result = 0; + struct em28xx_dvb *dvb; + + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + printk(KERN_INFO "em28xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + + em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + /* init frontend */ + switch (dev->model) { + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + dvb->frontend = dvb_attach(lgdt330x_attach, + &em2880_lgdt3303_dev, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + default: + printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n", + dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR + "%s/2: frontend initialization failed\n", + dev->name); + result = -EINVAL; + goto out_free; + } + + /* register everything */ + result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); + return 0; + +out_free: + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + kfree(dvb); + dev->dvb = NULL; + return result; +} + +static int dvb_fini(struct em28xx *dev) +{ + if (dev->dvb) { + unregister_dvb(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct em28xx_ops dvb_ops = { + .id = EM28XX_DVB, + .name = "Em28xx dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init em28xx_dvb_register(void) +{ + return em28xx_register_extension(&dvb_ops); +} + +static void __exit em28xx_dvb_unregister(void) +{ + em28xx_unregister_extension(&dvb_ops); +} + +module_init(em28xx_dvb_register); +module_exit(em28xx_dvb_unregister); diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index f1e52e5d1..c22e73019 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -41,11 +41,21 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -#define dprintk1(lvl,fmt, args...) if (i2c_debug>=lvl) do {\ - printk(fmt, ##args); } while (0) -#define dprintk2(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __func__ , ##args); } while (0) + +#define dprintk1(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(fmt, ##args); \ + } \ +} while (0) + +#define dprintk2(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) /* * em2800_i2c_send_max4() @@ -235,16 +245,16 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, return 0; for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; - dprintk2(2,"%s %s addr=%x len=%d:", + dprintk2(2, "%s %s addr=%x len=%d:", (msgs[i].flags & I2C_M_RD) ? "read" : "write", i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (!msgs[i].len) { /* no len: check only for device presence */ + if (!msgs[i].len) { /* no len: check only for device presence */ if (dev->is_em2800) rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); if (rc < 0) { - dprintk2(2," no device\n"); + dprintk2(2, " no device\n"); return rc; } @@ -258,14 +268,13 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = em28xx_i2c_recv_bytes(dev, addr, msgs[i].buf, msgs[i].len); - if (i2c_debug>=2) { - for (byte = 0; byte < msgs[i].len; byte++) { + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); - } } } else { /* write bytes */ - if (i2c_debug>=2) { + if (i2c_debug >= 2) { for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); } @@ -281,13 +290,13 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, } if (rc < 0) goto err; - if (i2c_debug>=2) + if (i2c_debug >= 2) printk("\n"); } return num; - err: - dprintk2(2," ERROR: %i\n", rc); +err: + dprintk2(2, " ERROR: %i\n", rc); return rc; } @@ -330,7 +339,9 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) return -1; buf = 0; - if (1 != (err = i2c_master_send(&dev->i2c_client, &buf, 1))) { + + err = i2c_master_send(&dev->i2c_client, &buf, 1); + if (err != 1) { printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", dev->name, err); return -1; @@ -403,8 +414,10 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) break; } printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", - em_eeprom->string_idx_table,em_eeprom->string1, - em_eeprom->string2,em_eeprom->string3); + em_eeprom->string_idx_table, + em_eeprom->string1, + em_eeprom->string2, + em_eeprom->string3); return 0; } @@ -441,58 +454,61 @@ static int attach_inform(struct i2c_client *client) struct em28xx *dev = client->adapter->algo_data; switch (client->addr << 1) { - case 0x86: - case 0x84: - case 0x96: - case 0x94: - { - struct v4l2_priv_tun_config tda9887_cfg; - - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); - break; - } - case 0x42: - dprintk1(1,"attach_inform: saa7114 detected.\n"); - break; - case 0x4a: - dprintk1(1,"attach_inform: saa7113 detected.\n"); - break; - case 0xa0: - dprintk1(1,"attach_inform: eeprom detected.\n"); - break; - case 0x60: - case 0x8e: - { - struct IR_i2c *ir = i2c_get_clientdata(client); - dprintk1(1,"attach_inform: IR detected (%s).\n",ir->phys); - em28xx_set_ir(dev,ir); - break; - } - case 0x80: - case 0x88: - dprintk1(1,"attach_inform: msp34xx detected.\n"); - break; - case 0xb8: - case 0xba: - dprintk1(1,"attach_inform: tvp5150 detected.\n"); - break; + case 0x86: + case 0x84: + case 0x96: + case 0x94: + { + struct v4l2_priv_tun_config tda9887_cfg; + + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = TUNER_TDA9887; + tun_setup.addr = client->addr; + + em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, + &tun_setup); + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, + &tda9887_cfg); + break; + } + case 0x42: + dprintk1(1, "attach_inform: saa7114 detected.\n"); + break; + case 0x4a: + dprintk1(1, "attach_inform: saa7113 detected.\n"); + break; + case 0xa0: + dprintk1(1, "attach_inform: eeprom detected.\n"); + break; + case 0x60: + case 0x8e: + { + struct IR_i2c *ir = i2c_get_clientdata(client); + dprintk1(1, "attach_inform: IR detected (%s).\n", + ir->phys); + em28xx_set_ir(dev, ir); + break; + } + case 0x80: + case 0x88: + dprintk1(1, "attach_inform: msp34xx detected.\n"); + break; + case 0xb8: + case 0xba: + dprintk1(1, "attach_inform: tvp5150 detected.\n"); + break; - default: - if (!dev->tuner_addr) - dev->tuner_addr = client->addr; + default: + if (!dev->tuner_addr) + dev->tuner_addr = client->addr; - dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1); + dprintk1(1, "attach inform: detected I2C address %x\n", + client->addr << 1); } @@ -522,7 +538,7 @@ static struct i2c_adapter em28xx_adap_template = { static struct i2c_client em28xx_client_template = { .name = "em28xx internal", -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) .flags = I2C_CLIENT_ALLOW_USE, #endif }; diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 5c78eceff..49c40f083 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -33,10 +33,12 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]"); +MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); -#define dprintk(fmt, arg...) if (ir_debug) \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg) +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ + } /* ----------------------------------------------------------------------- */ @@ -45,7 +47,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(&ir->c, &b, 1)) { dprintk("read error\n"); return -EIO; } @@ -75,29 +77,30 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char code; /* poll IR chip */ - if (2 != i2c_master_recv(&ir->c,buf,2)) + if (2 != i2c_master_recv(&ir->c, buf, 2)) return -EIO; /* Does eliminate repeated parity code */ - if (buf[1]==0xff) + if (buf[1] == 0xff) return 0; #if 0 /* avoid reapeating keystrokes */ - if (buf[1]==ir->old) + if (buf[1] == ir->old) return 0; #endif - ir->old=buf[1]; + ir->old = buf[1]; /* Rearranges bits to the right order */ - code= ((buf[0]&0x01)<<5) | /* 0010 0000 */ + code = ((buf[0]&0x01)<<5) | /* 0010 0000 */ ((buf[0]&0x02)<<3) | /* 0001 0000 */ ((buf[0]&0x04)<<1) | /* 0000 1000 */ ((buf[0]&0x08)>>1) | /* 0000 0100 */ ((buf[0]&0x10)>>3) | /* 0000 0010 */ ((buf[0]&0x20)>>5); /* 0000 0001 */ - dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",code,buf[0]); + dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", + code, buf[0]); /* return key */ *ir_key = code; @@ -112,15 +115,14 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ - if (3 != i2c_master_recv(&ir->c,buf,3)) { + if (3 != i2c_master_recv(&ir->c, buf, 3)) { dprintk("read error\n"); return -EIO; } dprintk("key %02x\n", buf[2]&0x3f); - if (buf[0]!=0x00){ + if (buf[0] != 0x00) return 0; - } *ir_key = buf[2]&0x3f; *ir_raw = buf[2]&0x3f; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h new file mode 100644 index 000000000..9058bed07 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -0,0 +1,88 @@ +#define EM_GPIO_0 (1 << 0) +#define EM_GPIO_1 (1 << 1) +#define EM_GPIO_2 (1 << 2) +#define EM_GPIO_3 (1 << 3) +#define EM_GPIO_4 (1 << 4) +#define EM_GPIO_5 (1 << 5) +#define EM_GPIO_6 (1 << 6) +#define EM_GPIO_7 (1 << 7) + +#define EM_GPO_0 (1 << 0) +#define EM_GPO_1 (1 << 1) +#define EM_GPO_2 (1 << 2) +#define EM_GPO_3 (1 << 3) + +/* em2800 registers */ +#define EM2800_R08_AUDIOSRC 0x08 + +/* em28xx registers */ + + /* GPIO/GPO registers */ +#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ +#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ + +#define EM28XX_R06_I2C_CLK 0x06 +#define EM28XX_R0A_CHIPID 0x0a +#define EM28XX_R0C_USBSUSP 0x0c /* */ + +#define EM28XX_R0E_AUDIOSRC 0x0e +#define EM28XX_R0F_XCLK 0x0f + +#define EM28XX_R10_VINMODE 0x10 +#define EM28XX_R11_VINCTRL 0x11 +#define EM28XX_R12_VINENABLE 0x12 /* */ + +#define EM28XX_R14_GAMMA 0x14 +#define EM28XX_R15_RGAIN 0x15 +#define EM28XX_R16_GGAIN 0x16 +#define EM28XX_R17_BGAIN 0x17 +#define EM28XX_R18_ROFFSET 0x18 +#define EM28XX_R19_GOFFSET 0x19 +#define EM28XX_R1A_BOFFSET 0x1a + +#define EM28XX_R1B_OFLOW 0x1b +#define EM28XX_R1C_HSTART 0x1c +#define EM28XX_R1D_VSTART 0x1d +#define EM28XX_R1E_CWIDTH 0x1e +#define EM28XX_R1F_CHEIGHT 0x1f + +#define EM28XX_R20_YGAIN 0x20 +#define EM28XX_R21_YOFFSET 0x21 +#define EM28XX_R22_UVGAIN 0x22 +#define EM28XX_R23_UOFFSET 0x23 +#define EM28XX_R24_VOFFSET 0x24 +#define EM28XX_R25_SHARPNESS 0x25 + +#define EM28XX_R26_COMPR 0x26 +#define EM28XX_R27_OUTFMT 0x27 + +#define EM28XX_R28_XMIN 0x28 +#define EM28XX_R29_XMAX 0x29 +#define EM28XX_R2A_YMIN 0x2a +#define EM28XX_R2B_YMAX 0x2b + +#define EM28XX_R30_HSCALELOW 0x30 +#define EM28XX_R31_HSCALEHIGH 0x31 +#define EM28XX_R32_VSCALELOW 0x32 +#define EM28XX_R33_VSCALEHIGH 0x33 + +#define EM28XX_R40_AC97LSB 0x40 +#define EM28XX_R41_AC97MSB 0x41 +#define EM28XX_R42_AC97ADDR 0x42 +#define EM28XX_R43_AC97BUSY 0x43 + +/* em202 registers */ +#define EM28XX_R02_MASTER_AC97 0x02 +#define EM28XX_R10_LINE_IN_AC97 0x10 +#define EM28XX_R14_VIDEO_AC97 0x14 + +/* register settings */ +#define EM2800_AUDIO_SRC_TUNER 0x0d +#define EM2800_AUDIO_SRC_LINE 0x0c +#define EM28XX_AUDIO_SRC_TUNER 0xc0 +#define EM28XX_AUDIO_SRC_LINE 0x80 + +/* FIXME: Need to be populated with the other chip ID's */ +enum em28xx_chip_id { + CHIP_ID_EM2883 = 36, +}; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 924f29214..093fef4bb 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1,5 +1,6 @@ /* - em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB video capture devices + em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB + video capture devices Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> Markus Rechberger <mrechberger@gmail.com> @@ -33,7 +34,7 @@ #include <linux/i2c.h> #include <linux/version.h> #include <linux/mm.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) #include <linux/mutex.h> #endif @@ -41,7 +42,7 @@ #include <media/v4l2-common.h> #include <media/msp3400.h> #include <media/tuner.h> -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) #include "i2c-compat.h" #endif @@ -63,14 +64,13 @@ static unsigned int isoc_debug; module_param(isoc_debug, int, 0644); MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); -#define em28xx_isocdbg(fmt, arg...) do {\ - if (isoc_debug) \ +#define em28xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - -/* Limits minimum and default number of buffers */ -#define EM28XX_MIN_BUF 4 -#define EM28XX_DEF_BUF 8 + dev->name, __func__ , ##arg); \ + } \ + } while (0) MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); @@ -83,13 +83,13 @@ static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) MODULE_PARM(card, "1-" __stringify(EM28XX_MAXBOARDS) "i"); MODULE_PARM(video_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); MODULE_PARM(vbi_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); MODULE_PARM(radio_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); #else -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) static int dummy; module_param_array(card, int, dummy, 0444); module_param_array(video_nr, int, dummy, 0444); @@ -108,8 +108,8 @@ MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); static unsigned int video_debug; -module_param(video_debug,int,0644); -MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; @@ -126,7 +126,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .step = 0x1, .default_value = 0x1f, .flags = 0, - },{ + }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Mute", @@ -313,11 +313,10 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, /* * Controls the isoc copy of each urb packet */ -static inline int em28xx_isoc_copy(struct urb *urb) +static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); unsigned char *outp = NULL; int i, len = 0, rc = 1; unsigned char *p; @@ -399,192 +398,6 @@ static inline int em28xx_isoc_copy(struct urb *urb) } /* ------------------------------------------------------------------ - URB control - ------------------------------------------------------------------*/ - -/* - * IRQ callback, called by URB callback - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -static void em28xx_irq_callback(struct urb *urb, struct pt_regs *regs) -#else -static void em28xx_irq_callback(struct urb *urb) -#endif -{ - struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - int rc, i; - - /* Copy data from URB */ - spin_lock(&dev->slock); - rc = em28xx_isoc_copy(urb); - spin_unlock(&dev->slock); - - /* Reset urb buffers */ - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - - urb->status = usb_submit_urb(urb, GFP_ATOMIC); - if (urb->status) { - em28xx_err("urb resubmit failed (error=%i)\n", - urb->status); - } -} - -/* - * Stop and Deallocate URBs - */ -static void em28xx_uninit_isoc(struct em28xx *dev) -{ - struct urb *urb; - int i; - - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); - - dev->isoc_ctl.nfields = -1; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; - if (urb) { - usb_kill_urb(urb); - usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_buffer_free(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } - usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; - } - dev->isoc_ctl.transfer_buffer[i] = NULL; - } - - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - - dev->isoc_ctl.num_bufs = 0; - - em28xx_capture_start(dev, 0); -} - -/* - * Allocate URBs and start IRQ - */ -static int em28xx_prepare_isoc(struct em28xx *dev, int max_packets, - int num_bufs) -{ - struct em28xx_dmaqueue *dma_q = &dev->vidq; - int i; - int sb_size, pipe; - struct urb *urb; - int j, k; - - em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); - - /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev); - - dev->isoc_ctl.num_bufs = num_bufs; - - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - em28xx_errdev("cannot alloc memory for usb buffers\n"); - return -ENOMEM; - } - - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.urb) { - em28xx_errdev("cannot allocate memory for usbtransfer\n"); - kfree(dev->isoc_ctl.urb); - return -ENOMEM; - } - - dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; - dev->isoc_ctl.buf = NULL; - - sb_size = max_packets * dev->isoc_ctl.max_pkt_size; - - /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); - if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev); - return -ENOMEM; - } - dev->isoc_ctl.urb[i] = urb; - - dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - em28xx_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt()?" while in int":""); - em28xx_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); - - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, 0x82); - usb_fill_int_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, - em28xx_irq_callback, dma_q, 1); - - urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP; - - k = 0; - for (j = 0; j < max_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; - } - } - - return 0; -} - -static int em28xx_start_thread(struct em28xx_dmaqueue *dma_q) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - int i, rc = 0; - - em28xx_videodbg("Called em28xx_start_thread\n"); - - init_waitqueue_head(&dma_q->wq); - - em28xx_capture_start(dev, 1); - - /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); - if (rc) { - em28xx_err("submit of urb %i failed (error=%i)\n", i, - rc); - em28xx_uninit_isoc(dev); - return rc; - } - } - - if (rc < 0) - return rc; - - return 0; -} - -/* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -592,6 +405,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct v4l2_frequency f; *size = 16 * fh->dev->width * fh->dev->height >> 3; if (0 == *count) @@ -600,6 +415,12 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) if (*count < EM28XX_MIN_BUF) *count = EM28XX_MIN_BUF; + /* Ask tuner to go to analog mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + + em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); + return 0; } @@ -637,7 +458,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx_fh *fh = vq->priv_data; struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vidq = &dev->vidq; int rc = 0, urb_init = 0; /* FIXME: It assumes depth = 16 */ @@ -661,12 +481,9 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = em28xx_prepare_isoc(dev, EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS); - if (rc < 0) - goto fail; - - rc = em28xx_start_thread(vidq); + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, dev->max_pkt_size, + em28xx_isoc_copy); if (rc < 0) goto fail; } @@ -692,7 +509,8 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) } -static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) { struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); struct em28xx_fh *fh = vq->priv_data; @@ -710,7 +528,7 @@ static struct videobuf_queue_ops em28xx_video_qops = { .buf_release = buffer_release, }; -/********************* v4l2 interface ******************************************/ +/********************* v4l2 interface **************************************/ /* * em28xx_config() @@ -726,9 +544,9 @@ static int em28xx_config(struct em28xx *dev) #if 1 /* enable vbi capturing */ -/* em28xx_write_regs_req(dev,0x00,0x0e,"\xC0",1); audio register */ -/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */ - em28xx_write_regs_req(dev,0x00,0x11,"\x51",1); +/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */ +/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */ + em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1); #endif dev->mute = 1; /* maybe not the right place... */ @@ -768,12 +586,15 @@ static void video_mux(struct em28xx *dev, int index) em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); if (dev->has_msp34xx) { - if (dev->i2s_speed) - em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); + if (dev->i2s_speed) { + em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, + &dev->i2s_speed); + } route.input = dev->ctl_ainput; route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, + &route); } em28xx_audio_analog_set(dev); @@ -1013,7 +834,7 @@ out: return rc; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1130,11 +951,11 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) index = dev->ctl_ainput; - if (index == 0) { + if (index == 0) strcpy(a->name, "Television"); - } else { + else strcpy(a->name, "Line In"); - } + a->capability = V4L2_AUDCAP_STEREO; a->index = index; @@ -1345,9 +1166,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int em28xx_reg_len(int reg) { switch (reg) { - case AC97LSB_REG: - case HSCALELOW_REG: - case VSCALELOW_REG: + case EM28XX_R40_AC97LSB: + case EM28XX_R30_HSCALELOW: + case EM28XX_R32_VSCALELOW: return 2; default: return 1; @@ -1749,7 +1570,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) { int minor = iminor(inode); int errCode = 0, radio = 0; - struct em28xx *h,*dev = NULL; + struct em28xx *h, *dev = NULL; struct em28xx_fh *fh; enum v4l2_buf_type fh_type = 0; @@ -1774,8 +1595,15 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) em28xx_videodbg("open minor=%d type=%s users=%d\n", minor, v4l2_type_names[fh_type], dev->users); - fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); +#if 0 + errCode = em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + if (errCode < 0) { + em28xx_errdev("Device locked on digital mode. Can't open analog\n"); + return -EBUSY; + } +#endif + fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); if (!fh) { em28xx_errdev("em28xx-video.c: Out of memory?!\n"); return -ENOMEM; @@ -1792,9 +1620,15 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev->hscale = 0; dev->vscale = 0; + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); em28xx_set_alternate(dev); em28xx_resolution_set(dev); + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_config_i2c(dev); + #if 0 /* device needs to be initialized before isoc transfer */ video_mux(dev, 0); @@ -1815,6 +1649,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); + return errCode; } @@ -1857,12 +1692,13 @@ static void em28xx_release_resources(struct em28xx *dev) usb_put_dev(dev->udev); /* Mark device as unused */ - em28xx_devused&=~(1<<dev->devno); + em28xx_devused &= ~(1<<dev->devno); } /* * em28xx_v4l2_close() - * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls */ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) { @@ -1893,6 +1729,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) /* do this before setting alternate! */ em28xx_uninit_isoc(dev); + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); /* set alternate 0 */ dev->alt = 0; @@ -1915,7 +1752,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) * will allocate buffers when called for the first time */ static ssize_t -em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, +em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) { struct em28xx_fh *fh = filp->private_data; @@ -2089,7 +1926,7 @@ static struct video_device em28xx_radio_template = { #endif }; -/******************************** usb interface *****************************************/ +/******************************** usb interface ******************************/ static LIST_HEAD(em28xx_extension_devlist); @@ -2184,10 +2021,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - errCode = em28xx_read_reg(dev, CHIPID_REG); - if (errCode >= 0) - em28xx_info("em28xx chip ID = %d\n", errCode); - em28xx_pre_card_setup(dev); errCode = em28xx_config(dev); @@ -2331,6 +2164,9 @@ static void request_module_async(struct work_struct *work) request_module("snd-usb-audio"); else request_module("em28xx-alsa"); + + if (dev->has_dvb) + request_module("em28xx-dvb"); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) @@ -2369,22 +2205,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum = interface->altsetting[0].desc.bInterfaceNumber; /* Check to see next free device and mark as used */ - nr=find_first_zero_bit(&em28xx_devused,EM28XX_MAXBOARDS); - em28xx_devused|=1<<nr; + nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + em28xx_devused |= 1<<nr; /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor,udev->descriptor.idProduct, + udev->descriptor.idVendor, + udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor,udev->descriptor.idProduct, + udev->descriptor.idVendor, + udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); @@ -2394,18 +2232,19 @@ static int em28xx_usb_probe(struct usb_interface *interface, if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } if (nr >= EM28XX_MAXBOARDS) { - printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS); - em28xx_devused&=~(1<<nr); + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + em28xx_devused &= ~(1<<nr); return -ENOMEM; } @@ -2413,7 +2252,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENOMEM; } @@ -2437,14 +2276,14 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; - dev->num_alt=uif->num_altsetting; - em28xx_info("Alternate settings: %i\n",dev->num_alt); -// dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* - dev->alt_max_pkt_size = kmalloc(32* - dev->num_alt,GFP_KERNEL); + dev->num_alt = uif->num_altsetting; + em28xx_info("Alternate settings: %i\n", dev->num_alt); +/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */ + dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); + if (dev->alt_max_pkt_size == NULL) { em28xx_errdev("out of memory!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); kfree(dev); return -ENOMEM; } @@ -2454,11 +2293,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - em28xx_info("Alternate setting %i, max size= %i\n",i, - dev->alt_max_pkt_size[i]); + em28xx_info("Alternate setting %i, max size= %i\n", i, + dev->alt_max_pkt_size[i]); } - if ((card[nr]>=0)&&(card[nr]<em28xx_bcount)) + if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) dev->model = card[nr]; /* allocate device struct */ @@ -2494,7 +2333,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_info("disconnecting %s\n", dev->vdev->name); - /* wait until all current v4l2 io is finished then deallocate resources */ + /* wait until all current v4l2 io is finished then deallocate + resources */ mutex_lock(&dev->lock); wake_up_interruptible_all(&dev->open); @@ -2531,7 +2371,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } static struct usb_driver em28xx_usb_driver = { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) .owner = THIS_MODULE, #endif .name = "em28xx", diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index cf6efe070..d31d11a5e 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -30,10 +30,38 @@ #include <media/videobuf-vmalloc.h> #include <linux/i2c.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) #include <linux/mutex.h> #endif #include <media/ir-kbd-i2c.h> +#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) +#include <media/videobuf-dvb.h> +#endif +#include "tuner-xc2028.h" +#include "em28xx-reg.h" + +/* Boards supported by driver */ +#define EM2800_BOARD_UNKNOWN 0 +#define EM2820_BOARD_UNKNOWN 1 +#define EM2820_BOARD_TERRATEC_CINERGY_250 2 +#define EM2820_BOARD_PINNACLE_USB_2 3 +#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 +#define EM2820_BOARD_MSI_VOX_USB_2 5 +#define EM2800_BOARD_TERRATEC_CINERGY_200 6 +#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 +#define EM2800_BOARD_KWORLD_USB2800 8 +#define EM2820_BOARD_PINNACLE_DVC_90 9 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 +#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 +#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 +#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 +#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 +#define EM2800_BOARD_VGEAR_POCKETTV 15 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 + +/* Limits minimum and default number of buffers */ +#define EM28XX_MIN_BUF 4 +#define EM28XX_DEF_BUF 8 /* maximum number of em28xx boards */ #define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ @@ -84,12 +112,20 @@ /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 +enum em28xx_mode { + EM28XX_MODE_UNDEFINED, + EM28XX_ANALOG_MODE, + EM28XX_DIGITAL_MODE, +}; + enum em28xx_stream_state { STREAM_OFF, STREAM_INTERRUPT, STREAM_ON, }; +struct em28xx; + struct em28xx_usb_isoc_ctl { /* max packet size of isoc transaction */ int max_pkt_size; @@ -119,6 +155,10 @@ struct em28xx_usb_isoc_ctl { /* Stores the number of received fields */ int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + }; struct em28xx_fmt { @@ -190,6 +230,12 @@ enum em28xx_decoder { EM28XX_SAA7114 }; +struct em28xx_reg_seq { + int reg; + unsigned char val, mask; + int sleep; +}; + struct em28xx_board { char *name; int vchannels; @@ -203,8 +249,7 @@ struct em28xx_board { unsigned int mts_firmware:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; - - unsigned int analog_gpio; + unsigned int has_dvb:1; enum em28xx_decoder decoder; @@ -237,7 +282,10 @@ enum em28xx_dev_state { #define EM28XX_NUM_AUDIO_PACKETS 64 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ #define EM28XX_CAPTURE_STREAM_EN 1 + +/* em28xx extensions */ #define EM28XX_AUDIO 0x10 +#define EM28XX_DVB 0x20 struct em28xx_audio { char name[50]; @@ -263,13 +311,24 @@ struct em28xx_audio { spinlock_t slock; }; +struct em28xx; + +struct em28xx_fh { + struct em28xx *dev; + unsigned int stream_on:1; /* Locks streams */ + int radio; + + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + /* main device struct */ struct em28xx { /* generic device properties */ char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ - unsigned int analog_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int has_tda9887:1; @@ -277,6 +336,16 @@ struct em28xx { unsigned int has_audio_class:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + + /* Some older em28xx chips needs a waiting time after writing */ + unsigned int wait_after_write; + + /* GPIO sequences for analog and digital mode */ + struct em28xx_reg_seq *analog_gpio, *digital_gpio; + + /* GPIO sequences for tuner callbacks */ + struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; int video_inputs; /* number of video inputs */ struct list_head devlist; @@ -309,7 +378,8 @@ struct em28xx { unsigned int video_bytesread; /* Number of bytes read */ unsigned long hash; /* eeprom hash - for boards with generic ID */ - unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ + unsigned long i2c_hash; /* i2c devicelist hash - + for boards with generic ID */ struct em28xx_audio *adev; @@ -320,7 +390,7 @@ struct em28xx { struct work_struct request_module_wk; /* locks */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) struct mutex lock; #else struct semaphore lock, fileop_lock; @@ -347,24 +417,21 @@ struct em28xx { struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc transfer */ /* helper funcs that call usb_control_msg */ - int (*em28xx_write_regs) (struct em28xx * dev, u16 reg, char *buf, - int len); - int (*em28xx_read_reg) (struct em28xx * dev, u16 reg); - int (*em28xx_read_reg_req_len) (struct em28xx * dev, u8 req, u16 reg, + int (*em28xx_write_regs) (struct em28xx *dev, u16 reg, + char *buf, int len); + int (*em28xx_read_reg) (struct em28xx *dev, u16 reg); + int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg, char *buf, int len); - int (*em28xx_write_regs_req) (struct em28xx * dev, u8 req, u16 reg, + int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg, char *buf, int len); - int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg); -}; + int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg); -struct em28xx_fh { - struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ - int radio; + enum em28xx_mode mode; - struct videobuf_queue vb_vidq; + /* Caches GPO and GPIO registers */ + unsigned char reg_gpo, reg_gpio; - enum v4l2_buf_type type; + struct em28xx_dvb *dvb; }; struct em28xx_ops { @@ -402,19 +469,26 @@ int em28xx_capture_start(struct em28xx *dev, int start); int em28xx_outfmt_set_yuv422(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); +int em28xx_init_isoc(struct em28xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); +void em28xx_uninit_isoc(struct em28xx *dev); +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); /* Provided by em28xx-video.c */ int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); /* Provided by em28xx-cards.c */ -extern int em2800_variant_detect(struct usb_device* udev,int model); +extern int em2800_variant_detect(struct usb_device *udev, int model); extern void em28xx_pre_card_setup(struct em28xx *dev); extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); +int em28xx_tuner_callback(void *ptr, int command, int arg); /* Provided by em28xx-input.c */ /* TODO: Check if the standard get_key handlers on ir-common can be used */ @@ -423,71 +497,6 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -/* em2800 registers */ -#define EM2800_AUDIOSRC_REG 0x08 - -/* em28xx registers */ -#define I2C_CLK_REG 0x06 -#define CHIPID_REG 0x0a -#define USBSUSP_REG 0x0c /* */ - -#define AUDIOSRC_REG 0x0e -#define XCLK_REG 0x0f - -#define VINMODE_REG 0x10 -#define VINCTRL_REG 0x11 -#define VINENABLE_REG 0x12 /* */ - -#define GAMMA_REG 0x14 -#define RGAIN_REG 0x15 -#define GGAIN_REG 0x16 -#define BGAIN_REG 0x17 -#define ROFFSET_REG 0x18 -#define GOFFSET_REG 0x19 -#define BOFFSET_REG 0x1a - -#define OFLOW_REG 0x1b -#define HSTART_REG 0x1c -#define VSTART_REG 0x1d -#define CWIDTH_REG 0x1e -#define CHEIGHT_REG 0x1f - -#define YGAIN_REG 0x20 -#define YOFFSET_REG 0x21 -#define UVGAIN_REG 0x22 -#define UOFFSET_REG 0x23 -#define VOFFSET_REG 0x24 -#define SHARPNESS_REG 0x25 - -#define COMPR_REG 0x26 -#define OUTFMT_REG 0x27 - -#define XMIN_REG 0x28 -#define XMAX_REG 0x29 -#define YMIN_REG 0x2a -#define YMAX_REG 0x2b - -#define HSCALELOW_REG 0x30 -#define HSCALEHIGH_REG 0x31 -#define VSCALELOW_REG 0x32 -#define VSCALEHIGH_REG 0x33 - -#define AC97LSB_REG 0x40 -#define AC97MSB_REG 0x41 -#define AC97ADDR_REG 0x42 -#define AC97BUSY_REG 0x43 - -/* em202 registers */ -#define MASTER_AC97 0x02 -#define LINE_IN_AC97 0x10 -#define VIDEO_AC97 0x14 - -/* register settings */ -#define EM2800_AUDIO_SRC_TUNER 0x0d -#define EM2800_AUDIO_SRC_LINE 0x0c -#define EM28XX_AUDIO_SRC_TUNER 0xc0 -#define EM28XX_AUDIO_SRC_LINE 0x80 - /* printk macros */ #define em28xx_err(fmt, arg...) do {\ @@ -504,80 +513,80 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, printk(KERN_WARNING "%s: "fmt,\ dev->name , ##arg); } while (0) -inline static int em28xx_compression_disable(struct em28xx *dev) +static inline int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ - return em28xx_write_regs(dev, COMPR_REG, "\x00", 1); + return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1); } -inline static int em28xx_contrast_get(struct em28xx *dev) +static inline int em28xx_contrast_get(struct em28xx *dev) { - return em28xx_read_reg(dev, YGAIN_REG) & 0x1f; + return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f; } -inline static int em28xx_brightness_get(struct em28xx *dev) +static inline int em28xx_brightness_get(struct em28xx *dev) { - return em28xx_read_reg(dev, YOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R21_YOFFSET); } -inline static int em28xx_saturation_get(struct em28xx *dev) +static inline int em28xx_saturation_get(struct em28xx *dev) { - return em28xx_read_reg(dev, UVGAIN_REG) & 0x1f; + return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f; } -inline static int em28xx_u_balance_get(struct em28xx *dev) +static inline int em28xx_u_balance_get(struct em28xx *dev) { - return em28xx_read_reg(dev, UOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R23_UOFFSET); } -inline static int em28xx_v_balance_get(struct em28xx *dev) +static inline int em28xx_v_balance_get(struct em28xx *dev) { - return em28xx_read_reg(dev, VOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R24_VOFFSET); } -inline static int em28xx_gamma_get(struct em28xx *dev) +static inline int em28xx_gamma_get(struct em28xx *dev) { - return em28xx_read_reg(dev, GAMMA_REG) & 0x3f; + return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f; } -inline static int em28xx_contrast_set(struct em28xx *dev, s32 val) +static inline int em28xx_contrast_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, YGAIN_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1); } -inline static int em28xx_brightness_set(struct em28xx *dev, s32 val) +static inline int em28xx_brightness_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, YOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1); } -inline static int em28xx_saturation_set(struct em28xx *dev, s32 val) +static inline int em28xx_saturation_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, UVGAIN_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1); } -inline static int em28xx_u_balance_set(struct em28xx *dev, s32 val) +static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, UOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1); } -inline static int em28xx_v_balance_set(struct em28xx *dev, s32 val) +static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, VOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1); } -inline static int em28xx_gamma_set(struct em28xx *dev, s32 val) +static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, GAMMA_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1); } /*FIXME: maxw should be dependent of alt mode */ -inline static unsigned int norm_maxw(struct em28xx *dev) +static inline unsigned int norm_maxw(struct em28xx *dev) { if (dev->max_range_640_480) return 640; @@ -585,7 +594,7 @@ inline static unsigned int norm_maxw(struct em28xx *dev) return 720; } -inline static unsigned int norm_maxh(struct em28xx *dev) +static inline unsigned int norm_maxh(struct em28xx *dev) { if (dev->max_range_640_480) return 480; diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index afe23f323..9a8795523 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -40,7 +40,6 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/workqueue.h> -#include <asm/semaphore.h> #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> @@ -531,8 +530,11 @@ static int ir_probe(struct i2c_adapter *adap) static const int probe_cx88[] = { 0x18, 0x6b, 0x71, -1 }; static const int probe_cx23885[] = { 0x6b, -1 }; const int *probe; - struct i2c_client *c; - unsigned char buf; + struct i2c_msg msg = { + .flags = I2C_M_RD, + .len = 0, + .buf = NULL, + }; int i, rc; switch (adap->id) { @@ -558,23 +560,17 @@ static int ir_probe(struct i2c_adapter *adap) return 0; } - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - - c->adapter = adap; for (i = 0; -1 != probe[i]; i++) { - c->addr = probe[i]; - rc = i2c_master_recv(c, &buf, 0); + msg.addr = probe[i]; + rc = i2c_transfer(adap, &msg, 1); dprintk(1,"probe 0x%02x @ %s: %s\n", probe[i], adap->name, - (0 == rc) ? "yes" : "no"); - if (0 == rc) { + (1 == rc) ? "yes" : "no"); + if (1 == rc) { ir_attach(adap, probe[i], 0, 0); break; } } - kfree(c); return 0; } diff --git a/linux/drivers/media/video/ivtv/Kconfig b/linux/drivers/media/video/ivtv/Kconfig index b6171702c..5d7ee8fcd 100644 --- a/linux/drivers/media/video/ivtv/Kconfig +++ b/linux/drivers/media/video/ivtv/Kconfig @@ -1,6 +1,8 @@ config VIDEO_IVTV tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" depends on VIDEO_V4L1 && VIDEO_V4L2 && PCI && I2C && EXPERIMENTAL + depends on INPUT # due to VIDEO_IR + depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT select FW_LOADER select VIDEO_IR diff --git a/linux/drivers/media/video/ivtv/Makefile b/linux/drivers/media/video/ivtv/Makefile index a0389014f..26ce0d6ea 100644 --- a/linux/drivers/media/video/ivtv/Makefile +++ b/linux/drivers/media/video/ivtv/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.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 diff --git a/linux/drivers/media/video/ivtv/ivtv-cards.c b/linux/drivers/media/video/ivtv/ivtv-cards.c index 92e4b85f9..03455fb30 100644 --- a/linux/drivers/media/video/ivtv/ivtv-cards.c +++ b/linux/drivers/media/video/ivtv/ivtv-cards.c @@ -40,6 +40,8 @@ #define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define V4L2_STD_NOT_MN (V4L2_STD_PAL|V4L2_STD_SECAM) + /* usual i2c tuner addresses to probe */ static struct ivtv_card_tuner_i2c ivtv_i2c_std = { .radio = { I2C_CLIENT_END }, @@ -298,7 +300,7 @@ static const struct ivtv_card ivtv_card_mpg600 = { .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, .tuners = { /* The PAL tuner is confirmed */ - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_mpg600, @@ -339,7 +341,7 @@ static const struct ivtv_card ivtv_card_mpg160 = { .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, .tuners = { - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_mpg160, @@ -375,7 +377,7 @@ static const struct ivtv_card ivtv_card_pg600 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, }, .tuners = { - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_pg600, @@ -416,7 +418,7 @@ static const struct ivtv_card ivtv_card_avc2410 = { on the country/region setting of the user to decide which tuner is available. */ .tuners = { - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FM1236_MK3 }, { .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 }, @@ -490,7 +492,7 @@ static const struct ivtv_card ivtv_card_tg5000tv = { .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, .composite = 0x0010, .svideo = 0x0020 }, .tuners = { - { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_tg5000tv, .i2c = &ivtv_i2c_std, @@ -521,7 +523,7 @@ static const struct ivtv_card ivtv_card_va2000 = { { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, }, .tuners = { - { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_va2000, .i2c = &ivtv_i2c_std, @@ -565,7 +567,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc = { .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, .f44100 = 0x4000, .f48000 = 0x8000 }, .tuners = { - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .pci_list = ivtv_pci_cx23416gyc, @@ -597,7 +599,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogr = { .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, .f44100 = 0x4000, .f48000 = 0x8000 }, .tuners = { - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .i2c = &ivtv_i2c_std, @@ -627,7 +629,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = { .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, .f44100 = 0x4000, .f48000 = 0x8000 }, .tuners = { - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .i2c = &ivtv_i2c_std, @@ -667,7 +669,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx = { .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, .tuners = { /* This card has the Panasonic VP27 tuner */ - { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, }, .pci_list = ivtv_pci_gv_mvprx, .i2c = &ivtv_i2c_std, @@ -704,7 +706,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx2e = { .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, .tuners = { /* This card has the Panasonic VP27 tuner */ - { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, }, .pci_list = ivtv_pci_gv_mvprx2e, .i2c = &ivtv_i2c_std, @@ -739,7 +741,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd = { .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 }, .tuners = { /* This card has a Philips FQ1216ME MK3 tuner */ - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, }, .pci_list = ivtv_pci_gotview_pci_dvd, .i2c = &ivtv_i2c_std, @@ -778,7 +780,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = { .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, .tuners = { /* This card has a Philips FQ1216ME MK5 tuner */ - { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, }, .pci_list = ivtv_pci_gotview_pci_dvd2, .i2c = &ivtv_i2c_std, @@ -856,7 +858,7 @@ static const struct ivtv_card ivtv_card_dctmvtvp1 = { .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, .composite = 0x0010, .svideo = 0x0020}, .tuners = { - { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_dctmvtvp1, .i2c = &ivtv_i2c_std, @@ -875,6 +877,7 @@ static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = { static const struct ivtv_card ivtv_card_pg600v2 = { .type = IVTV_CARD_PG600V2, .name = "Yuan PG600-2, GotView PCI DVD Lite", + .comment = "only Composite and S-Video inputs are supported, not the tuner\n", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, @@ -894,7 +897,7 @@ static const struct ivtv_card ivtv_card_pg600v2 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, - .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */ + .xceive_pin = 12, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, @@ -941,7 +944,7 @@ static const struct ivtv_card ivtv_card_club3d = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, - .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */ + .xceive_pin = 12, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, @@ -965,34 +968,22 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, -#if 0 - /* XC2028 support has problems with fw loading on this card, - so don't support this tuner for this card until this issue - has been solved. */ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739, .video_inputs = { - { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 }, - { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 }, + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, /* enable line-in */ - .gpio_init = { .direction = 0xf000, .initial_value = 0x5000 }, + .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, + .xceive_pin = 10, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, -#else - .hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739, - .video_inputs = { - { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 }, - { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 }, - }, - .audio_inputs = { - { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, - }, - .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, /* enable line-in */ -#endif .pci_list = ivtv_pci_avertv_mce116, .i2c = &ivtv_i2c_std, }; @@ -1030,7 +1021,7 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, .tuners = { /* This card has a Partsnic PTI-5NF05 tuner */ - { .std = V4L2_STD_525_60, .tuner = TUNER_TCL_2002N }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_TCL_2002N }, }, .pci_list = ivtv_pci_aver_pvr150, .i2c = &ivtv_i2c_radio, @@ -1098,12 +1089,83 @@ static const struct ivtv_card ivtv_card_asus_falcon2 = { }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER }, .tuners = { - { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .pci_list = ivtv_pci_asus_falcon2, .i2c = &ivtv_i2c_std, }; +/* ------------------------------------------------------------------------- */ + +/* AVerMedia M104 miniPCI card */ + +static const struct ivtv_card_pci_info ivtv_pci_aver_m104[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc136 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_aver_m104 = { + .type = IVTV_CARD_AVER_M104, + .name = "AVerMedia M104", + .comment = "Not yet supported!\n", + .v4l2_capabilities = 0, /*IVTV_CAP_ENCODER,*/ + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739, + .video_inputs = { + { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 }, + { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, + /* enable line-in + reset tuner */ + .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, + .xceive_pin = 10, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, + .pci_list = ivtv_pci_aver_m104, + .i2c = &ivtv_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + +/* Buffalo PC-MV5L/PCI cards */ + +static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_buffalo = { + .type = IVTV_CARD_BUFFALO_MV5L, + .name = "Buffalo PC-MV5L/PCI", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .xceive_pin = 12, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, + .pci_list = ivtv_pci_buffalo, + .i2c = &ivtv_i2c_std, +}; + static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_pvr250, &ivtv_card_pvr350, @@ -1129,6 +1191,8 @@ static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_asus_falcon2, &ivtv_card_aver_pvr150, &ivtv_card_aver_ezmaker, + &ivtv_card_aver_m104, + &ivtv_card_buffalo, /* Variations of standard cards but with the same PCI IDs. These cards must come last in this list. */ @@ -1160,7 +1224,8 @@ int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input) if (index >= itv->nof_inputs) return -EINVAL; input->index = index; - strcpy(input->name, input_strs[card_input->video_type - 1]); + strlcpy(input->name, input_strs[card_input->video_type - 1], + sizeof(input->name)); input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ? V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); input->audioset = (1 << itv->nof_audio_inputs) - 1; @@ -1177,7 +1242,7 @@ int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output) if (index >= itv->card->nof_outputs) return -EINVAL; output->index = index; - strcpy(output->name, card_output->name); + strlcpy(output->name, card_output->name, sizeof(output->name)); output->type = V4L2_OUTPUT_TYPE_ANALOG; output->audioset = 1; output->std = V4L2_STD_ALL; @@ -1196,7 +1261,8 @@ int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio) memset(audio, 0, sizeof(*audio)); if (index >= itv->nof_audio_inputs) return -EINVAL; - strcpy(audio->name, input_strs[aud_input->audio_type - 1]); + strlcpy(audio->name, input_strs[aud_input->audio_type - 1], + sizeof(audio->name)); audio->index = index; audio->capability = V4L2_AUDCAP_STEREO; return 0; @@ -1207,6 +1273,6 @@ int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud memset(aud_output, 0, sizeof(*aud_output)); if (itv->card->video_outputs == NULL || index != 0) return -EINVAL; - strcpy(aud_output->name, "A/V Audio Out"); + strlcpy(aud_output->name, "A/V Audio Out", sizeof(aud_output->name)); return 0; } diff --git a/linux/drivers/media/video/ivtv/ivtv-cards.h b/linux/drivers/media/video/ivtv/ivtv-cards.h index 9186fa2ee..381af1bce 100644 --- a/linux/drivers/media/video/ivtv/ivtv-cards.h +++ b/linux/drivers/media/video/ivtv/ivtv-cards.h @@ -48,7 +48,9 @@ #define IVTV_CARD_ASUS_FALCON2 21 /* ASUS Falcon2 */ #define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */ #define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */ -#define IVTV_CARD_LAST 23 +#define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */ +#define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */ +#define IVTV_CARD_LAST 25 /* Variants of existing cards but with the same PCI IDs. The driver detects these based on other device information. @@ -244,6 +246,7 @@ struct ivtv_card_tuner_i2c { struct ivtv_card { int type; char *name; + char *comment; u32 v4l2_capabilities; u32 hw_video; /* hardware used to process video */ u32 hw_audio; /* hardware used to process audio */ @@ -256,6 +259,7 @@ struct ivtv_card { int nof_outputs; const struct ivtv_card_output *video_outputs; u8 gr_config; /* config byte for the ghost reduction device */ + u8 xceive_pin; /* XCeive tuner GPIO reset pin */ /* GPIO card-specific settings */ struct ivtv_gpio_init gpio_init; diff --git a/linux/drivers/media/video/ivtv/ivtv-controls.c b/linux/drivers/media/video/ivtv/ivtv-controls.c index 8c02fa661..c7e449f63 100644 --- a/linux/drivers/media/video/ivtv/ivtv-controls.c +++ b/linux/drivers/media/video/ivtv/ivtv-controls.c @@ -181,12 +181,12 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm return 0; } /* Need sliced data for mpeg insertion */ - if (get_service_set(itv->vbi.sliced_in) == 0) { + if (ivtv_get_service_set(itv->vbi.sliced_in) == 0) { if (itv->is_60hz) itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; else itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; - expand_service_set(itv->vbi.sliced_in, itv->is_50hz); + ivtv_expand_service_set(itv->vbi.sliced_in, itv->is_50hz); } return 0; } diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index fdb2e5848..094f5524f 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -190,6 +190,8 @@ MODULE_PARM_DESC(cardtype, "\t\t\t22 = ASUS Falcon2\n" "\t\t\t23 = AverMedia PVR-150 Plus\n" "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n" + "\t\t\t25 = AverMedia M104 (not yet working)\n" + "\t\t\t26 = Buffalo PC-MV5L/PCI\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60"); @@ -856,6 +858,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, return 0; } +#ifdef MODULE static u32 ivtv_request_module(struct ivtv *itv, u32 hw, const char *name, u32 id) { @@ -868,14 +871,16 @@ static u32 ivtv_request_module(struct ivtv *itv, u32 hw, IVTV_DEBUG_INFO("Loaded module %s\n", name); return hw; } +#endif static void ivtv_load_and_init_modules(struct ivtv *itv) { u32 hw = itv->card->hw_all; unsigned i; +#ifdef MODULE /* load modules */ -#ifndef CONFIG_VIDEO_TUNER +#ifndef CONFIG_MEDIA_TUNER hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER); #endif #ifndef CONFIG_VIDEO_CX25840 @@ -914,6 +919,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) #ifndef CONFIG_VIDEO_M52790 hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790); #endif +#endif /* check which i2c devices are actually found */ for (i = 0; i < 32; i++) { @@ -1018,7 +1024,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, ivtv_cards[ivtv_cards_active] = itv; itv->dev = dev; itv->num = ivtv_cards_active++; - snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num); + snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num); IVTV_INFO("Initializing card #%d\n", itv->num); spin_unlock(&ivtv_cards_lock); @@ -1052,7 +1058,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IVTV_ENCODER_SIZE); if (!itv->enc_mem) { IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); - IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + IVTV_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); retval = -ENOMEM; goto free_mem; } @@ -1064,7 +1070,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IVTV_DECODER_SIZE); if (!itv->dec_mem) { IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); - IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + IVTV_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); retval = -ENOMEM; goto free_mem; } @@ -1080,7 +1086,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); if (!itv->reg_mem) { IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); - IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + IVTV_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); retval = -ENOMEM; goto free_io; } @@ -1101,6 +1107,13 @@ static int __devinit ivtv_probe(struct pci_dev *dev, The PCI IDs are not always reliable. */ ivtv_process_eeprom(itv); } + if (itv->card->comment) + IVTV_INFO("%s", itv->card->comment); + if (itv->card->v4l2_capabilities == 0) { + /* card was detected but is not supported */ + retval = -ENODEV; + goto free_i2c; + } if (itv->std == 0) { itv->std = V4L2_STD_NTSC_M; @@ -1199,13 +1212,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std); } - retval = ivtv_streams_setup(itv); - if (retval) { - IVTV_ERR("Error %d setting up streams\n", retval); - goto free_i2c; - } - - IVTV_DEBUG_IRQ("Masking interrupts\n"); /* clear interrupt mask, effectively disabling interrupts */ ivtv_set_irq_mask(itv, 0xffffffff); @@ -1214,32 +1220,38 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv); if (retval) { IVTV_ERR("Failed to register irq %d\n", retval); - goto free_streams; + goto free_i2c; + } + + retval = ivtv_streams_setup(itv); + if (retval) { + IVTV_ERR("Error %d setting up streams\n", retval); + goto free_irq; } retval = ivtv_streams_register(itv); if (retval) { IVTV_ERR("Error %d registering devices\n", retval); - goto free_irq; + goto free_streams; } IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name); return 0; - free_irq: +free_streams: + ivtv_streams_cleanup(itv, 1); +free_irq: free_irq(itv->dev->irq, (void *)itv); - free_streams: - ivtv_streams_cleanup(itv); - free_i2c: +free_i2c: exit_ivtv_i2c(itv); - free_io: +free_io: ivtv_iounmap(itv); - free_mem: +free_mem: release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); if (itv->has_cx23415) release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); - free_workqueue: +free_workqueue: destroy_workqueue(itv->irq_work_queues); - err: +err: if (retval == 0) retval = -ENODEV; IVTV_ERR("Error %d on initialization\n", retval); @@ -1370,7 +1382,7 @@ static void ivtv_remove(struct pci_dev *pci_dev) flush_workqueue(itv->irq_work_queues); destroy_workqueue(itv->irq_work_queues); - ivtv_streams_cleanup(itv); + ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); exit_ivtv_i2c(itv); diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index a7640c49f..f2fa434b6 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -755,8 +755,10 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) IVTV_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (eof || s->q_full.length || s->q_io.length) + if (s->q_full.length || s->q_io.length) return POLLIN | POLLRDNORM; + if (eof) + return POLLHUP; return 0; } @@ -985,6 +987,8 @@ int ivtv_v4l2_open(struct inode *inode, struct file *filp) /* Find which card this open was on */ spin_lock(&ivtv_cards_lock); for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { + if (ivtv_cards[x] == NULL) + continue; /* find out which stream this open was on */ for (y = 0; y < IVTV_MAX_STREAMS; y++) { s = &ivtv_cards[x]->streams[y]; diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c index 688cd3856..bc22905ea 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.c +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c @@ -128,20 +128,17 @@ int ivtv_reset_tuner_gpio(void *dev, int cmd, int value) { struct i2c_algo_bit_data *algo = dev; struct ivtv *itv = algo->data; - int curdir, curout; + u32 curout; if (cmd != XC2028_TUNER_RESET) return 0; IVTV_DEBUG_INFO("Resetting tuner\n"); curout = read_reg(IVTV_REG_GPIO_OUT); - curdir = read_reg(IVTV_REG_GPIO_DIR); - curdir |= (1 << 12); /* GPIO bit 12 */ - - curout &= ~(1 << 12); + curout &= ~(1 << itv->card->xceive_pin); write_reg(curout, IVTV_REG_GPIO_OUT); schedule_timeout_interruptible(msecs_to_jiffies(1)); - curout |= (1 << 12); + curout |= 1 << itv->card->xceive_pin; write_reg(curout, IVTV_REG_GPIO_OUT); schedule_timeout_interruptible(msecs_to_jiffies(1)); return 0; @@ -149,15 +146,20 @@ int ivtv_reset_tuner_gpio(void *dev, int cmd, int value) void ivtv_gpio_init(struct ivtv *itv) { - if (itv->card->gpio_init.direction == 0) + u16 pin = 0; + + if (itv->card->xceive_pin) + pin = 1 << itv->card->xceive_pin; + + if ((itv->card->gpio_init.direction | pin) == 0) return; IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); /* init output data then direction */ - write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT); - write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR); + write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); } static struct v4l2_queryctrl gpio_ctrl_mute = { diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index 77b4f960b..db9243b08 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -168,7 +168,8 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) return -1; id = hw_driverids[idx]; memset(&info, 0, sizeof(info)); - strcpy(info.driver_name, hw_drivernames[idx]); + strlcpy(info.driver_name, hw_drivernames[idx], + sizeof(info.driver_name)); info.addr = hw_addrs[idx]; for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {} diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index a5da513d6..7101a0ea5 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -38,7 +38,7 @@ #include <linux/dvb/audio.h> #include <linux/i2c-id.h> -u16 service2vbi(int type) +u16 ivtv_service2vbi(int type) { switch (type) { case V4L2_SLICED_TELETEXT_B: @@ -88,7 +88,7 @@ static u16 select_service_from_set(int field, int line, u16 set, int is_pal) return 0; } -void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) { u16 set = fmt->service_set; int f, l; @@ -115,7 +115,7 @@ static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) return set != 0; } -u16 get_service_set(struct v4l2_sliced_vbi_format *fmt) +u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt) { int f, l; u16 set = 0; @@ -243,20 +243,31 @@ static int ivtv_validate_speed(int cur_speed, int new_speed) int fact = new_speed < 0 ? -1 : 1; int s; - if (new_speed < 0) new_speed = -new_speed; - if (cur_speed < 0) cur_speed = -cur_speed; + if (cur_speed == 0) + cur_speed = 1000; + if (new_speed < 0) + new_speed = -new_speed; + if (cur_speed < 0) + cur_speed = -cur_speed; if (cur_speed <= new_speed) { - if (new_speed > 1500) return fact * 2000; - if (new_speed > 1000) return fact * 1500; + if (new_speed > 1500) + return fact * 2000; + if (new_speed > 1000) + return fact * 1500; } else { - if (new_speed >= 2000) return fact * 2000; - if (new_speed >= 1500) return fact * 1500; - if (new_speed >= 1000) return fact * 1000; - } - if (new_speed == 0) return 1000; - if (new_speed == 1 || new_speed == 1000) return fact * new_speed; + if (new_speed >= 2000) + return fact * 2000; + if (new_speed >= 1500) + return fact * 1500; + if (new_speed >= 1000) + return fact * 1000; + } + if (new_speed == 0) + return 1000; + if (new_speed == 1 || new_speed == 1000) + return fact * new_speed; s = new_speed; new_speed = 1000 / new_speed; @@ -455,7 +466,7 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; } - vbifmt->service_set = get_service_set(vbifmt); + vbifmt->service_set = ivtv_get_service_set(vbifmt); break; } @@ -470,12 +481,12 @@ static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fm if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; - expand_service_set(vbifmt, itv->is_50hz); + ivtv_expand_service_set(vbifmt, itv->is_50hz); break; } itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); - vbifmt->service_set = get_service_set(vbifmt); + vbifmt->service_set = ivtv_get_service_set(vbifmt); break; } case V4L2_BUF_TYPE_VBI_OUTPUT: @@ -635,9 +646,9 @@ static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); if (vbifmt->service_set) - expand_service_set(vbifmt, itv->is_50hz); + ivtv_expand_service_set(vbifmt, itv->is_50hz); set = check_service_set(vbifmt, itv->is_50hz); - vbifmt->service_set = get_service_set(vbifmt); + vbifmt->service_set = ivtv_get_service_set(vbifmt); if (!set_fmt) return 0; @@ -747,9 +758,9 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void struct v4l2_capability *vcap = arg; memset(vcap, 0, sizeof(*vcap)); - strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */ - strcpy(vcap->card, itv->card_name); /* card type */ - strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */ + strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); + strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); + strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info)); vcap->version = IVTV_DRIVER_VERSION; /* version */ vcap->capabilities = itv->v4l2_cap; /* capabilities */ @@ -1023,7 +1034,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void ivtv_std_60hz : ivtv_std_50hz; vs->index = idx; vs->id = enum_stds[idx].std; - strcpy(vs->name, enum_stds[idx].name); + strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name)); break; } @@ -1107,10 +1118,10 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { - strcpy(vt->name, "ivtv Radio Tuner"); + strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); vt->type = V4L2_TUNER_RADIO; } else { - strcpy(vt->name, "ivtv TV Tuner"); + strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name)); vt->type = V4L2_TUNER_ANALOG_TV; } break; diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.h b/linux/drivers/media/video/ivtv/ivtv-ioctl.h index a03351b68..4e67f0ed1 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.h +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.h @@ -21,9 +21,9 @@ #ifndef IVTV_IOCTL_H #define IVTV_IOCTL_H -u16 service2vbi(int type); -void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); -u16 get_service_set(struct v4l2_sliced_vbi_format *fmt); +u16 ivtv_service2vbi(int type); +void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); +u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt); int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg); diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index 0e417d860..41066e87f 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -390,7 +390,7 @@ static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s) ivtv_stream_sync_for_device(s); write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR); write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); - itv->dma_timer.expires = jiffies + msecs_to_jiffies(100); + itv->dma_timer.expires = jiffies + msecs_to_jiffies(300); add_timer(&itv->dma_timer); } @@ -406,7 +406,7 @@ static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s) ivtv_stream_sync_for_device(s); write_reg(s->sg_handle, IVTV_REG_DECDMAADDR); write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); - itv->dma_timer.expires = jiffies + msecs_to_jiffies(100); + itv->dma_timer.expires = jiffies + msecs_to_jiffies(300); add_timer(&itv->dma_timer); } diff --git a/linux/drivers/media/video/ivtv/ivtv-queue.c b/linux/drivers/media/video/ivtv/ivtv-queue.c index 3e1deec67..fc8b1eaa3 100644 --- a/linux/drivers/media/video/ivtv/ivtv-queue.c +++ b/linux/drivers/media/video/ivtv/ivtv-queue.c @@ -203,14 +203,14 @@ int ivtv_stream_alloc(struct ivtv_stream *s) s->dma != PCI_DMA_NONE ? "DMA " : "", s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); - s->sg_pending = kzalloc(SGsize, GFP_KERNEL); + s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN); if (s->sg_pending == NULL) { IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name); return -ENOMEM; } s->sg_pending_size = 0; - s->sg_processing = kzalloc(SGsize, GFP_KERNEL); + s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN); if (s->sg_processing == NULL) { IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name); kfree(s->sg_pending); @@ -219,7 +219,8 @@ int ivtv_stream_alloc(struct ivtv_stream *s) } s->sg_processing_size = 0; - s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element), GFP_KERNEL); + s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element), + GFP_KERNEL|__GFP_NOWARN); if (s->sg_dma == NULL) { IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name); kfree(s->sg_pending); @@ -235,11 +236,12 @@ int ivtv_stream_alloc(struct ivtv_stream *s) /* allocate stream buffers. Initially all buffers are in q_free. */ for (i = 0; i < s->buffers; i++) { - struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL); + struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), + GFP_KERNEL|__GFP_NOWARN); if (buf == NULL) break; - buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL); + buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN); if (buf->buf == NULL) { kfree(buf); break; diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 4ab8d3683..01f546083 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -44,23 +44,29 @@ #include "ivtv-streams.h" static const struct file_operations ivtv_v4l2_enc_fops = { - .owner = THIS_MODULE, - .read = ivtv_v4l2_read, - .write = ivtv_v4l2_write, - .open = ivtv_v4l2_open, - .ioctl = ivtv_v4l2_ioctl, - .release = ivtv_v4l2_close, - .poll = ivtv_v4l2_enc_poll, + .owner = THIS_MODULE, + .read = ivtv_v4l2_read, + .write = ivtv_v4l2_write, + .open = ivtv_v4l2_open, + .ioctl = ivtv_v4l2_ioctl, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .compat_ioctl = v4l_compat_ioctl32, +#endif + .release = ivtv_v4l2_close, + .poll = ivtv_v4l2_enc_poll, }; static const struct file_operations ivtv_v4l2_dec_fops = { - .owner = THIS_MODULE, - .read = ivtv_v4l2_read, - .write = ivtv_v4l2_write, - .open = ivtv_v4l2_open, - .ioctl = ivtv_v4l2_ioctl, - .release = ivtv_v4l2_close, - .poll = ivtv_v4l2_dec_poll, + .owner = THIS_MODULE, + .read = ivtv_v4l2_read, + .write = ivtv_v4l2_write, + .open = ivtv_v4l2_open, + .ioctl = ivtv_v4l2_ioctl, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .compat_ioctl = v4l_compat_ioctl32, +#endif + .release = ivtv_v4l2_close, + .poll = ivtv_v4l2_dec_poll, }; #define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ @@ -244,7 +250,7 @@ int ivtv_streams_setup(struct ivtv *itv) return 0; /* One or more streams could not be initialized. Clean 'em all up. */ - ivtv_streams_cleanup(itv); + ivtv_streams_cleanup(itv, 0); return -ENOMEM; } @@ -304,12 +310,12 @@ int ivtv_streams_register(struct ivtv *itv) return 0; /* One or more streams could not be initialized. Clean 'em all up. */ - ivtv_streams_cleanup(itv); + ivtv_streams_cleanup(itv, 1); return -ENOMEM; } /* Unregister v4l2 devices */ -void ivtv_streams_cleanup(struct ivtv *itv) +void ivtv_streams_cleanup(struct ivtv *itv, int unregister) { int type; @@ -322,8 +328,11 @@ void ivtv_streams_cleanup(struct ivtv *itv) continue; ivtv_stream_free(&itv->streams[type]); - /* Unregister device */ - video_unregister_device(vdev); + /* Unregister or release device */ + if (unregister) + video_unregister_device(vdev); + else + video_device_release(vdev); } } diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.h b/linux/drivers/media/video/ivtv/ivtv-streams.h index 3d76a415f..a653a5136 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.h +++ b/linux/drivers/media/video/ivtv/ivtv-streams.h @@ -23,7 +23,7 @@ int ivtv_streams_setup(struct ivtv *itv); int ivtv_streams_register(struct ivtv *itv); -void ivtv_streams_cleanup(struct ivtv *itv); +void ivtv_streams_cleanup(struct ivtv *itv, int unregister); /* Capture related */ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s); diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c index c151bcf55..71798f0da 100644 --- a/linux/drivers/media/video/ivtv/ivtv-vbi.c +++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c @@ -169,7 +169,8 @@ static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) linemask[0] |= (1 << l); else linemask[1] |= (1 << (l - 32)); - dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id); + dst[sd + 12 + line * 43] = + ivtv_service2vbi(itv->vbi.sliced_data[i].id); memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42); line++; } diff --git a/linux/drivers/media/video/ivtv/ivtv-version.h b/linux/drivers/media/video/ivtv/ivtv-version.h index 0f1d4cc4b..442f43f11 100644 --- a/linux/drivers/media/video/ivtv/ivtv-version.h +++ b/linux/drivers/media/video/ivtv/ivtv-version.h @@ -22,7 +22,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 -#define IVTV_DRIVER_VERSION_MINOR 2 +#define IVTV_DRIVER_VERSION_MINOR 3 #define IVTV_DRIVER_VERSION_PATCHLEVEL 0 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index 393d917cd..a9417f6e4 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -908,7 +908,7 @@ static void ivtv_yuv_init(struct ivtv *itv) } /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ - yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL); + yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN); if (yi->blanking_ptr) { yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE); } else { @@ -1098,8 +1098,8 @@ void ivtv_yuv_setup_stream_frame(struct ivtv *itv) ivtv_yuv_next_free(itv); /* Copy V4L2 parameters to an ivtv_dma_frame struct... */ - dma_args.y_source = 0L; - dma_args.uv_source = 0L; + dma_args.y_source = NULL; + dma_args.uv_source = NULL; dma_args.src.left = 0; dma_args.src.top = 0; dma_args.src.width = yi->v4l2_src_w; diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c index 3b23fc05f..e8dbee443 100644 --- a/linux/drivers/media/video/ivtv/ivtvfb.c +++ b/linux/drivers/media/video/ivtv/ivtvfb.c @@ -367,6 +367,87 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count); } +static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + void *dst; + int err = 0; + unsigned long total_size; + struct ivtv *itv = (struct ivtv *) info->par; + unsigned long dma_offset = + IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; + unsigned long dma_size; + u16 lead = 0, tail = 0; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + total_size = info->screen_size; + + if (total_size == 0) + total_size = info->fix.smem_len; + + if (p > total_size) + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + + if (count + p > total_size) { + if (!err) + err = -ENOSPC; + + count = total_size - p; + } + + dst = (void __force *) (info->screen_base + p); + + if (info->fbops->fb_sync) + info->fbops->fb_sync(info); + + if (!access_ok(VERIFY_READ, buf, count)) { + IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n", + (unsigned long)buf); + err = -EFAULT; + } + + if (!err) { + /* If transfer size > threshold and both src/dst + addresses are aligned, use DMA */ + if (count >= 4096 && ((u32)buf & 3) == ((u32)dst & 3)) { + /* Odd address = can't DMA. Align */ + if ((u32)dst & 3) { + lead = 4 - ((u32)dst & 3); + memcpy(dst, buf, lead); + buf += lead; + dst += lead; + } + /* DMA resolution is 32 bits */ + if ((count - lead) & 3) + tail = (count - lead) & 3; + /* DMA the data */ + dma_size = count - lead - tail; + err = ivtvfb_prep_dec_dma_to_device(itv, + p + lead + dma_offset, (void *)buf, dma_size); + dst += dma_size; + buf += dma_size; + /* Copy any leftover data */ + if (tail) + memcpy(dst, buf, tail); + } else { + memcpy(dst, buf, count); + } + } + + if (!err) + *ppos += count; + + return (err) ? err : count; +} + static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { DEFINE_WAIT(wait); @@ -532,7 +613,7 @@ static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n"); memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - strcpy(fix->id, "cx23415 TV out"); + strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id)); fix->smem_start = oi->video_pbase; fix->smem_len = oi->video_buffer_size; fix->type = FB_TYPE_PACKED_PIXELS; @@ -824,6 +905,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) static struct fb_ops ivtvfb_ops = { .owner = THIS_MODULE, + .fb_write = ivtvfb_write, .fb_check_var = ivtvfb_check_var, .fb_set_par = ivtvfb_set_par, .fb_setcolreg = ivtvfb_setcolreg, @@ -948,7 +1030,8 @@ static int ivtvfb_init_vidmode(struct ivtv *itv) } /* Allocate the pseudo palette */ - oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); + oi->ivtvfb_info.pseudo_palette = + kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN); if (!oi->ivtvfb_info.pseudo_palette) { IVTVFB_ERR("abort, unable to alloc pseudo pallete\n"); @@ -1056,7 +1139,8 @@ static int ivtvfb_init_card(struct ivtv *itv) return -EBUSY; } - itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC); + itv->osd_info = kzalloc(sizeof(struct osd_info), + GFP_ATOMIC|__GFP_NOWARN); if (itv->osd_info == NULL) { IVTVFB_ERR("Failed to allocate memory for osd_info\n"); return -ENOMEM; diff --git a/linux/drivers/media/video/m52790.c b/linux/drivers/media/video/m52790.c index fa49d8f3e..6cca77680 100644 --- a/linux/drivers/media/video/m52790.c +++ b/linux/drivers/media/video/m52790.c @@ -140,7 +140,8 @@ static int m52790_command(struct i2c_client *client, unsigned int cmd, /* i2c implementation */ -static int m52790_probe(struct i2c_client *client) +static int m52790_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct m52790_state *state; diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index f96330be7..ade4fc567 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -43,15 +43,10 @@ #include <linux/meye.h> MODULE_AUTHOR("Stelian Pop <stelian@popies.net>"); -MODULE_DESCRIPTION("v4l/v4l2 driver for the MotionEye camera"); +MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera"); MODULE_LICENSE("GPL"); MODULE_VERSION(MEYE_DRIVER_VERSION); -/* force usage of V4L1 API */ -static int forcev4l1; /* = 0 */ -module_param(forcev4l1, int, 0644); -MODULE_PARM_DESC(forcev4l1, "force use of V4L1 instead of V4L2"); - /* number of grab buffers */ static unsigned int gbuffers = 2; module_param(gbuffers, int, 0444); @@ -881,803 +876,735 @@ static int meye_release(struct inode *inode, struct file *file) return 0; } -static int meye_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int meyeioc_g_params(struct meye_params *p) { - switch (cmd) { + *p = meye.params; + return 0; +} - case VIDIOCGCAP: { - struct video_capability *b = arg; - strcpy(b->name,meye.video_dev->name); - b->type = VID_TYPE_CAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = 640; - b->maxheight = 480; - b->minwidth = 320; - b->minheight = 240; - break; - } +static int meyeioc_s_params(struct meye_params *jp) +{ + if (jp->subsample > 1) + return -EINVAL; - case VIDIOCGCHAN: { - struct video_channel *v = arg; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - if (v->channel != 0) - return -EINVAL; - strcpy(v->name,"Camera"); - break; - } + if (jp->quality > 10) + return -EINVAL; - case VIDIOCSCHAN: { - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - break; - } + if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) + return -EINVAL; - case VIDIOCGPICT: { - struct video_picture *p = arg; - *p = meye.picture; - break; - } + if (jp->framerate > 31) + return -EINVAL; - case VIDIOCSPICT: { - struct video_picture *p = arg; - if (p->depth != 16) - return -EINVAL; - if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV) - return -EINVAL; - mutex_lock(&meye.lock); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, - p->brightness >> 10); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, - p->hue >> 10); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, - p->colour >> 10); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, - p->contrast >> 10); - meye.picture = *p; - mutex_unlock(&meye.lock); - break; - } + mutex_lock(&meye.lock); - case VIDIOCSYNC: { - int *i = arg; - int unused; + if (meye.params.subsample != jp->subsample || + meye.params.quality != jp->quality) + mchip_hic_stop(); /* need restart */ + + meye.params = *jp; + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, + meye.params.sharpness); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, + meye.params.agc); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, + meye.params.picture); + mutex_unlock(&meye.lock); - if (*i < 0 || *i >= gbuffers) - return -EINVAL; + return 0; +} - mutex_lock(&meye.lock); +static int meyeioc_qbuf_capt(int *nb) +{ + if (!meye.grab_fbuffer) + return -EINVAL; - switch (meye.grab_buffer[*i].state) { + if (*nb >= gbuffers) + return -EINVAL; - case MEYE_BUF_UNUSED: - mutex_unlock(&meye.lock); - return -EINVAL; - case MEYE_BUF_USING: - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { - mutex_unlock(&meye.lock); - return -EINTR; - } - /* fall through */ - case MEYE_BUF_DONE: - meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; - kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); - } - mutex_unlock(&meye.lock); - break; + if (*nb < 0) { + /* stop capture */ + mchip_hic_stop(); + return 0; } - case VIDIOCMCAPTURE: { - struct video_mmap *vm = arg; - int restart = 0; - - if (vm->frame >= gbuffers || vm->frame < 0) - return -EINVAL; - if (vm->format != VIDEO_PALETTE_YUV422 && vm->format != VIDEO_PALETTE_YUYV) - return -EINVAL; - if (vm->height * vm->width * 2 > gbufsize) - return -EINVAL; - if (!meye.grab_fbuffer) - return -EINVAL; - if (meye.grab_buffer[vm->frame].state != MEYE_BUF_UNUSED) - return -EBUSY; - - mutex_lock(&meye.lock); - if (vm->width == 640 && vm->height == 480) { - if (meye.params.subsample) { - meye.params.subsample = 0; - restart = 1; - } - } else if (vm->width == 320 && vm->height == 240) { - if (!meye.params.subsample) { - meye.params.subsample = 1; - restart = 1; - } - } else { - mutex_unlock(&meye.lock); - return -EINVAL; - } + if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) + return -EBUSY; - if (restart || meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT) - mchip_continuous_start(); - meye.grab_buffer[vm->frame].state = MEYE_BUF_USING; - kfifo_put(meye.grabq, (unsigned char *)&vm->frame, sizeof(int)); - mutex_unlock(&meye.lock); - break; - } + mutex_lock(&meye.lock); - case VIDIOCGMBUF: { - struct video_mbuf *vm = arg; - int i; + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + mchip_cont_compression_start(); - memset(vm, 0 , sizeof(*vm)); - vm->size = gbufsize * gbuffers; - vm->frames = gbuffers; - for (i = 0; i < gbuffers; i++) - vm->offsets[i] = i * gbufsize; - break; - } + meye.grab_buffer[*nb].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)nb, sizeof(int)); + mutex_unlock(&meye.lock); - case MEYEIOC_G_PARAMS: { - struct meye_params *p = arg; - *p = meye.params; - break; - } + return 0; +} - case MEYEIOC_S_PARAMS: { - struct meye_params *jp = arg; - if (jp->subsample > 1) - return -EINVAL; - if (jp->quality > 10) - return -EINVAL; - if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) - return -EINVAL; - if (jp->framerate > 31) - return -EINVAL; - mutex_lock(&meye.lock); - if (meye.params.subsample != jp->subsample || - meye.params.quality != jp->quality) - mchip_hic_stop(); /* need restart */ - meye.params = *jp; - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, - meye.params.sharpness); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, - meye.params.agc); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, - meye.params.picture); - mutex_unlock(&meye.lock); - break; - } +static int meyeioc_sync(struct file *file, void *fh, int *i) +{ + int unused; - case MEYEIOC_QBUF_CAPT: { - int *nb = arg; - - if (!meye.grab_fbuffer) - return -EINVAL; - if (*nb >= gbuffers) - return -EINVAL; - if (*nb < 0) { - /* stop capture */ - mchip_hic_stop(); - return 0; - } - if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) - return -EBUSY; - mutex_lock(&meye.lock); - if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) - mchip_cont_compression_start(); - meye.grab_buffer[*nb].state = MEYE_BUF_USING; - kfifo_put(meye.grabq, (unsigned char *)nb, sizeof(int)); + if (*i < 0 || *i >= gbuffers) + return -EINVAL; + + mutex_lock(&meye.lock); + switch (meye.grab_buffer[*i].state) { + + case MEYE_BUF_UNUSED: mutex_unlock(&meye.lock); - break; + return -EINVAL; + case MEYE_BUF_USING: + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { + mutex_unlock(&meye.lock); + return -EINTR; + } + /* fall through */ + case MEYE_BUF_DONE: + meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; + kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); } + *i = meye.grab_buffer[*i].size; + mutex_unlock(&meye.lock); + return 0; +} - case MEYEIOC_SYNC: { - int *i = arg; - int unused; +static int meyeioc_stillcapt(void) +{ + if (!meye.grab_fbuffer) + return -EINVAL; - if (*i < 0 || *i >= gbuffers) - return -EINVAL; + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; - mutex_lock(&meye.lock); - switch (meye.grab_buffer[*i].state) { + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + mchip_take_picture(); - case MEYE_BUF_UNUSED: - mutex_unlock(&meye.lock); - return -EINVAL; - case MEYE_BUF_USING: - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { - mutex_unlock(&meye.lock); - return -EINTR; - } - /* fall through */ - case MEYE_BUF_DONE: - meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; - kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); - } - *i = meye.grab_buffer[*i].size; - mutex_unlock(&meye.lock); - break; - } + mchip_get_picture(meye.grab_fbuffer, + mchip_hsize() * mchip_vsize() * 2); - case MEYEIOC_STILLCAPT: { + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); - if (!meye.grab_fbuffer) - return -EINVAL; - if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) - return -EBUSY; - mutex_lock(&meye.lock); - meye.grab_buffer[0].state = MEYE_BUF_USING; + return 0; +} + +static int meyeioc_stilljcapt(int *len) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + *len = -1; + + while (*len == -1) { mchip_take_picture(); - mchip_get_picture( - meye.grab_fbuffer, - mchip_hsize() * mchip_vsize() * 2); - meye.grab_buffer[0].state = MEYE_BUF_DONE; - mutex_unlock(&meye.lock); - break; + *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); } - case MEYEIOC_STILLJCAPT: { - int *len = arg; - - if (!meye.grab_fbuffer) - return -EINVAL; - if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) - return -EBUSY; - mutex_lock(&meye.lock); - meye.grab_buffer[0].state = MEYE_BUF_USING; - *len = -1; - while (*len == -1) { - mchip_take_picture(); - *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); - } - meye.grab_buffer[0].state = MEYE_BUF_DONE; - mutex_unlock(&meye.lock); - break; - } + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + return 0; +} - case VIDIOC_QUERYCAP: { - struct v4l2_capability *cap = arg; +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, "meye"); + strcpy(cap->card, "meye"); + sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); - if (forcev4l1) - return -EINVAL; + cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + + MEYE_DRIVER_MINORVERSION; - memset(cap, 0, sizeof(*cap)); - strcpy(cap->driver, "meye"); - strcpy(cap->card, "meye"); - sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); - cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + - MEYE_DRIVER_MINORVERSION; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING; - break; - } + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; - case VIDIOC_ENUMINPUT: { - struct v4l2_input *i = arg; + memset(i, 0, sizeof(*i)); + i->index = 0; + strcpy(i->name, "Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; - if (i->index != 0) - return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = 0; - strcpy(i->name, "Camera"); - i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + if (i != 0) + return -EINVAL; + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *c) +{ + switch (c->id) { + + case V4L2_CID_BRIGHTNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Brightness"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_HUE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Hue"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_CONTRAST: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Contrast"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_SATURATION: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Saturation"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_AGC: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Agc"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 48; + c->flags = 0; break; + case V4L2_CID_MEYE_SHARPNESS: + case V4L2_CID_SHARPNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Sharpness"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + + /* Continue to report legacy private SHARPNESS ctrl but + * say it is disabled in preference to ctrl in the spec + */ + c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : + V4L2_CTRL_FLAG_DISABLED; + break; + case V4L2_CID_PICTURE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Picture"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 0; + c->flags = 0; + break; + case V4L2_CID_JPEGQUAL: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "JPEG quality"); + c->minimum = 0; + c->maximum = 10; + c->step = 1; + c->default_value = 8; + c->flags = 0; + break; + case V4L2_CID_FRAMERATE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Framerate"); + c->minimum = 0; + c->maximum = 31; + c->step = 1; + c->default_value = 0; + c->flags = 0; + break; + default: + return -EINVAL; } - case VIDIOC_G_INPUT: { - int *i = arg; + return 0; +} - *i = 0; +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +{ + mutex_lock(&meye.lock); + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); + meye.picture.brightness = c->value << 10; + break; + case V4L2_CID_HUE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, c->value); + meye.picture.hue = c->value << 10; + break; + case V4L2_CID_CONTRAST: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); + meye.picture.contrast = c->value << 10; + break; + case V4L2_CID_SATURATION: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); + meye.picture.colour = c->value << 10; + break; + case V4L2_CID_AGC: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, c->value); + meye.params.agc = c->value; + break; + case V4L2_CID_SHARPNESS: + case V4L2_CID_MEYE_SHARPNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); + meye.params.sharpness = c->value; + break; + case V4L2_CID_PICTURE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); + meye.params.picture = c->value; + break; + case V4L2_CID_JPEGQUAL: + meye.params.quality = c->value; + break; + case V4L2_CID_FRAMERATE: + meye.params.framerate = c->value; break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; } + mutex_unlock(&meye.lock); - case VIDIOC_S_INPUT: { - int *i = arg; + return 0; +} - if (*i != 0) - return -EINVAL; +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +{ + mutex_lock(&meye.lock); + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = meye.picture.brightness >> 10; + break; + case V4L2_CID_HUE: + c->value = meye.picture.hue >> 10; + break; + case V4L2_CID_CONTRAST: + c->value = meye.picture.contrast >> 10; + break; + case V4L2_CID_SATURATION: + c->value = meye.picture.colour >> 10; + break; + case V4L2_CID_AGC: + c->value = meye.params.agc; + break; + case V4L2_CID_SHARPNESS: + case V4L2_CID_MEYE_SHARPNESS: + c->value = meye.params.sharpness; break; + case V4L2_CID_PICTURE: + c->value = meye.params.picture; + break; + case V4L2_CID_JPEGQUAL: + c->value = meye.params.quality; + break; + case V4L2_CID_FRAMERATE: + c->value = meye.params.framerate; + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; } + mutex_unlock(&meye.lock); - case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *c = arg; + return 0; +} - switch (c->id) { +static int vidioc_enum_fmt_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index > 1) + return -EINVAL; - case V4L2_CID_BRIGHTNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Brightness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_HUE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Hue"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_CONTRAST: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Contrast"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_SATURATION: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Saturation"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_AGC: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Agc"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 48; - c->flags = 0; - break; - case V4L2_CID_MEYE_SHARPNESS: - case V4L2_CID_SHARPNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Sharpness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - - /* Continue to report legacy private SHARPNESS ctrl but - * say it is disabled in preference to ctrl in the spec - */ - c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : - V4L2_CTRL_FLAG_DISABLED; - break; - case V4L2_CID_PICTURE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Picture"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - case V4L2_CID_JPEGQUAL: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "JPEG quality"); - c->minimum = 0; - c->maximum = 10; - c->step = 1; - c->default_value = 8; - c->flags = 0; - break; - case V4L2_CID_FRAMERATE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Framerate"); - c->minimum = 0; - c->maximum = 31; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - default: - return -EINVAL; - } - break; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (f->index == 0) { + /* standard YUV 422 capture */ + memset(f, 0, sizeof(*f)); + f->index = 0; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = 0; + strcpy(f->description, "YUV422"); + f->pixelformat = V4L2_PIX_FMT_YUYV; + } else { + /* compressed MJPEG capture */ + memset(f, 0, sizeof(*f)); + f->index = 1; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, "MJPEG"); + f->pixelformat = V4L2_PIX_FMT_MJPEG; } - case VIDIOC_S_CTRL: { - struct v4l2_control *c = arg; + return 0; +} - mutex_lock(&meye.lock); - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); - meye.picture.brightness = c->value << 10; - break; - case V4L2_CID_HUE: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAHUE, c->value); - meye.picture.hue = c->value << 10; - break; - case V4L2_CID_CONTRAST: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); - meye.picture.contrast = c->value << 10; - break; - case V4L2_CID_SATURATION: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); - meye.picture.colour = c->value << 10; - break; - case V4L2_CID_AGC: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAAGC, c->value); - meye.params.agc = c->value; - break; - case V4L2_CID_SHARPNESS: - case V4L2_CID_MEYE_SHARPNESS: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); - meye.params.sharpness = c->value; - break; - case V4L2_CID_PICTURE: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); - meye.params.picture = c->value; - break; - case V4L2_CID_JPEGQUAL: - meye.params.quality = c->value; - break; - case V4L2_CID_FRAMERATE: - meye.params.framerate = c->value; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); - break; +static int vidioc_try_fmt_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; } - case VIDIOC_G_CTRL: { - struct v4l2_control *c = arg; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; - mutex_lock(&meye.lock); - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = meye.picture.brightness >> 10; - break; - case V4L2_CID_HUE: - c->value = meye.picture.hue >> 10; - break; - case V4L2_CID_CONTRAST: - c->value = meye.picture.contrast >> 10; - break; - case V4L2_CID_SATURATION: - c->value = meye.picture.colour >> 10; - break; - case V4L2_CID_AGC: - c->value = meye.params.agc; - break; - case V4L2_CID_SHARPNESS: - case V4L2_CID_MEYE_SHARPNESS: - c->value = meye.params.sharpness; - break; - case V4L2_CID_PICTURE: - c->value = meye.params.picture; - break; - case V4L2_CID_JPEGQUAL: - c->value = meye.params.quality; - break; - case V4L2_CID_FRAMERATE: - c->value = meye.params.framerate; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); + return 0; +} + +static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + default: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case MCHIP_HIC_MODE_CONT_COMP: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; break; } - case VIDIOC_ENUM_FMT: { - struct v4l2_fmtdesc *f = arg; - - if (f->index > 1) - return -EINVAL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->index == 0) { - /* standard YUV 422 capture */ - memset(f, 0, sizeof(*f)); - f->index = 0; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = 0; - strcpy(f->description, "YUV422"); - f->pixelformat = V4L2_PIX_FMT_YUYV; - } else { - /* compressed MJPEG capture */ - memset(f, 0, sizeof(*f)); - f->index = 1; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, "MJPEG"); - f->pixelformat = V4L2_PIX_FMT_MJPEG; - } - break; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = mchip_hsize(); + f->fmt.pix.height = mchip_vsize(); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + mutex_lock(&meye.lock); + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + meye.params.subsample = 1; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + meye.params.subsample = 0; } - case VIDIOC_TRY_FMT: { - struct v4l2_format *f = arg; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - f->fmt.pix.field = V4L2_FIELD_NONE; - if (f->fmt.pix.width <= 320) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - } else { - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - } - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + break; + case V4L2_PIX_FMT_MJPEG: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; break; } - case VIDIOC_G_FMT: { - struct v4l2_format *f = arg; + mutex_unlock(&meye.lock); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - switch (meye.mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - default: - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - break; - case MCHIP_HIC_MODE_CONT_COMP: - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; - break; - } - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = mchip_hsize(); - f->fmt.pix.height = mchip_vsize(); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; - break; - } + return 0; +} - case VIDIOC_S_FMT: { - struct v4l2_format *f = arg; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - f->fmt.pix.field = V4L2_FIELD_NONE; - mutex_lock(&meye.lock); - if (f->fmt.pix.width <= 320) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - meye.params.subsample = 1; - } else { - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - meye.params.subsample = 0; - } - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: - meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; - break; - case V4L2_PIX_FMT_MJPEG: - meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; - break; - } - mutex_unlock(&meye.lock); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int i; - break; - } + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - case VIDIOC_REQBUFS: { - struct v4l2_requestbuffers *req = arg; - int i; + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (meye.grab_fbuffer && req->count == gbuffers) { - /* already allocated, no modifications */ - break; - } - mutex_lock(&meye.lock); - if (meye.grab_fbuffer) { - for (i = 0; i < gbuffers; i++) - if (meye.vma_use_count[i]) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - rvfree(meye.grab_fbuffer, gbuffers * gbufsize); - meye.grab_fbuffer = NULL; - } - gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); - req->count = gbuffers; - meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); - if (!meye.grab_fbuffer) { - printk(KERN_ERR "meye: v4l framebuffer allocation" - " failed\n"); - mutex_unlock(&meye.lock); - return -ENOMEM; - } - for (i = 0; i < gbuffers; i++) - meye.vma_use_count[i] = 0; - mutex_unlock(&meye.lock); - break; + if (meye.grab_fbuffer && req->count == gbuffers) { + /* already allocated, no modifications */ + return 0; } - case VIDIOC_QUERYBUF: { - struct v4l2_buffer *buf = arg; - int index = buf->index; - - if (index < 0 || index >= gbuffers) - return -EINVAL; - memset(buf, 0, sizeof(*buf)); - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->index = index; - buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; - if (meye.grab_buffer[index].state == MEYE_BUF_USING) - buf->flags |= V4L2_BUF_FLAG_QUEUED; - if (meye.grab_buffer[index].state == MEYE_BUF_DONE) - buf->flags |= V4L2_BUF_FLAG_DONE; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[index].timestamp; - buf->sequence = meye.grab_buffer[index].sequence; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = index * gbufsize; - buf->length = gbufsize; - break; + mutex_lock(&meye.lock); + if (meye.grab_fbuffer) { + for (i = 0; i < gbuffers; i++) + if (meye.vma_use_count[i]) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + rvfree(meye.grab_fbuffer, gbuffers * gbufsize); + meye.grab_fbuffer = NULL; } - case VIDIOC_QBUF: { - struct v4l2_buffer *buf = arg; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (buf->index < 0 || buf->index >= gbuffers) - return -EINVAL; - if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) - return -EINVAL; - mutex_lock(&meye.lock); - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - meye.grab_buffer[buf->index].state = MEYE_BUF_USING; - kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int)); + gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); + req->count = gbuffers; + meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); + + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation" + " failed\n"); mutex_unlock(&meye.lock); - break; + return -ENOMEM; } - case VIDIOC_DQBUF: { - struct v4l2_buffer *buf = arg; - int reqnr; + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; + mutex_unlock(&meye.lock); - mutex_lock(&meye.lock); - if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - kfifo_len(meye.doneq) != 0) < 0) { - mutex_unlock(&meye.lock); - return -EINTR; - } - if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr, - sizeof(int))) { - mutex_unlock(&meye.lock); - return -EBUSY; - } - if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - buf->index = reqnr; - buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[reqnr].timestamp; - buf->sequence = meye.grab_buffer[reqnr].sequence; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = reqnr * gbufsize; - buf->length = gbufsize; - meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + return 0; +} + +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + int index = buf->index; + + if (index < 0 || index >= gbuffers) + return -EINVAL; + + memset(buf, 0, sizeof(*buf)); + + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->index = index; + buf->bytesused = meye.grab_buffer[index].size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + + if (meye.grab_buffer[index].state == MEYE_BUF_USING) + buf->flags |= V4L2_BUF_FLAG_QUEUED; + + if (meye.grab_buffer[index].state == MEYE_BUF_DONE) + buf->flags |= V4L2_BUF_FLAG_DONE; + + buf->field = V4L2_FIELD_NONE; + buf->timestamp = meye.grab_buffer[index].timestamp; + buf->sequence = meye.grab_buffer[index].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * gbufsize; + buf->length = gbufsize; + + return 0; +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index < 0 || buf->index >= gbuffers) + return -EINVAL; + + if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) + return -EINVAL; + + mutex_lock(&meye.lock); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + meye.grab_buffer[buf->index].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int)); + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + int reqnr; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&meye.lock); + + if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { mutex_unlock(&meye.lock); - break; + return -EAGAIN; } - case VIDIOC_STREAMON: { - mutex_lock(&meye.lock); - switch (meye.mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - mchip_continuous_start(); - break; - case MCHIP_HIC_MODE_CONT_COMP: - mchip_cont_compression_start(); - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } + if (wait_event_interruptible(meye.proc_list, + kfifo_len(meye.doneq) != 0) < 0) { mutex_unlock(&meye.lock); - break; + return -EINTR; } - case VIDIOC_STREAMOFF: { - int i; + if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr, + sizeof(int))) { + mutex_unlock(&meye.lock); + return -EBUSY; + } - mutex_lock(&meye.lock); - mchip_hic_stop(); - kfifo_reset(meye.grabq); - kfifo_reset(meye.doneq); - for (i = 0; i < MEYE_MAX_BUFNBRS; i++) - meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { mutex_unlock(&meye.lock); - break; + return -EINVAL; } - /* - * XXX what about private snapshot ioctls ? - * Do they need to be converted to V4L2 ? - */ + buf->index = reqnr; + buf->bytesused = meye.grab_buffer[reqnr].size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = meye.grab_buffer[reqnr].timestamp; + buf->sequence = meye.grab_buffer[reqnr].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = reqnr * gbufsize; + buf->length = gbufsize; + meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + mutex_unlock(&meye.lock); + + return 0; +} +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + mutex_lock(&meye.lock); + + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; default: - return -ENOIOCTLCMD; + mutex_unlock(&meye.lock); + return -EINVAL; } + mutex_unlock(&meye.lock); + return 0; } -static int meye_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { - return video_usercopy(inode, file, cmd, arg, meye_do_ioctl); + mutex_lock(&meye.lock); + mchip_hic_stop(); + kfifo_reset(meye.grabq); + kfifo_reset(meye.doneq); + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + + mutex_unlock(&meye.lock); + return 0; +} + +static int vidioc_default(struct file *file, void *fh, int cmd, void *arg) +{ + switch (cmd) { + case MEYEIOC_G_PARAMS: + return meyeioc_g_params((struct meye_params *) arg); + + case MEYEIOC_S_PARAMS: + return meyeioc_s_params((struct meye_params *) arg); + + case MEYEIOC_QBUF_CAPT: + return meyeioc_qbuf_capt((int *) arg); + + case MEYEIOC_SYNC: + return meyeioc_sync(file, fh, (int *) arg); + + case MEYEIOC_STILLCAPT: + return meyeioc_stillcapt(); + + case MEYEIOC_STILLJCAPT: + return meyeioc_stilljcapt((int *) arg); + + default: + return -EINVAL; + } + } static unsigned int meye_poll(struct file *file, poll_table *wait) @@ -1765,7 +1692,7 @@ static const struct file_operations meye_fops = { .open = meye_open, .release = meye_release, .mmap = meye_mmap, - .ioctl = meye_ioctl, + .ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, #endif @@ -1780,6 +1707,24 @@ static struct video_device meye_template = { .fops = &meye_fops, .release = video_device_release, .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_default = vidioc_default, }; #ifdef CONFIG_PM diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 716f30eb5..4ae9a5acf 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -849,7 +849,7 @@ static int msp_resume(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int msp_probe(struct i2c_client *client) +static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct msp_state *state; int (*thread_func)(void *data) = NULL; diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index 3fb5f63df..ee4349954 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -12,15 +12,12 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/log2.h> +#include <linux/gpio.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> -#ifdef CONFIG_MT9M001_PCA9536_SWITCH -#include <asm/gpio.h> -#endif - /* mt9m001 i2c address 0x5d * The platform has to define i2c_board_info * and call i2c_register_board_info() */ @@ -372,7 +369,7 @@ static int mt9m001_set_register(struct soc_camera_device *icd, } #endif -const struct v4l2_queryctrl mt9m001_controls[] = { +static const struct v4l2_queryctrl mt9m001_controls[] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -620,7 +617,8 @@ static void mt9m001_video_remove(struct soc_camera_device *icd) soc_camera_video_stop(&mt9m001->icd); } -static int mt9m001_probe(struct i2c_client *client) +static int mt9m001_probe(struct i2c_client *client, + const struct i2c_device_id *did) { struct mt9m001 *mt9m001; struct soc_camera_device *icd; @@ -696,12 +694,19 @@ static int mt9m001_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id mt9m001_id[] = { + { "mt9m001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9m001_id); + static struct i2c_driver mt9m001_i2c_driver = { .driver = { .name = "mt9m001", }, .probe = mt9m001_probe, .remove = mt9m001_remove, + .id_table = mt9m001_id, }; static int __init mt9m001_mod_init(void) diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index d4b9e2744..1658fe590 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -13,15 +13,12 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/log2.h> +#include <linux/gpio.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> -#ifdef CONFIG_MT9M001_PCA9536_SWITCH -#include <asm/gpio.h> -#endif - /* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c * The platform has to define i2c_board_info * and call i2c_register_board_info() */ @@ -91,7 +88,7 @@ static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { struct mt9v022 { struct i2c_client *client; struct soc_camera_device icd; - int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ int switch_gpio; u16 chip_control; unsigned char datawidth; @@ -452,7 +449,7 @@ static int mt9v022_set_register(struct soc_camera_device *icd, } #endif -const struct v4l2_queryctrl mt9v022_controls[] = { +static const struct v4l2_queryctrl mt9v022_controls[] = { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -745,7 +742,8 @@ static void mt9v022_video_remove(struct soc_camera_device *icd) soc_camera_video_stop(&mt9v022->icd); } -static int mt9v022_probe(struct i2c_client *client) +static int mt9v022_probe(struct i2c_client *client, + const struct i2c_device_id *did) { struct mt9v022 *mt9v022; struct soc_camera_device *icd; @@ -818,12 +816,19 @@ static int mt9v022_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id mt9v022_id[] = { + { "mt9v022", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9v022_id); + static struct i2c_driver mt9v022_i2c_driver = { .driver = { .name = "mt9v022", }, .probe = mt9v022_probe, .remove = mt9v022_remove, + .id_table = mt9v022_id, }; static int __init mt9v022_mod_init(void) diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index cba851d18..9676c1d6f 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -41,7 +41,6 @@ #include <linux/slab.h> #include <linux/ctype.h> #include <linux/pagemap.h> -#include <asm/semaphore.h> #include <asm/processor.h> #include <linux/mm.h> #include <linux/device.h> @@ -5838,7 +5837,7 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) goto error; memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); - ov->vdev->dev = &dev->dev; + ov->vdev->dev = &intf->dev; video_set_drvdata(ov->vdev, ov); for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 6fc1b8be1..4482b2c72 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -1,6 +1,8 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_V4L2 && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C + depends on VIDEO_MEDIA # Avoids pvrusb = Y / DVB = M + depends on HOTPLUG # due to FW_LOADER select FW_LOADER select VIDEO_TUNER select VIDEO_TVEEPROM @@ -9,6 +11,7 @@ config VIDEO_PVRUSB2 select VIDEO_CX25840 select VIDEO_MSP3400 select VIDEO_WM8775 + select VIDEO_CS53L32A ---help--- This is a video4linux driver for Conexant 23416 based usb2 personal video recorder devices. @@ -16,32 +19,6 @@ config VIDEO_PVRUSB2 To compile this driver as a module, choose M here: the module will be called pvrusb2 -config VIDEO_PVRUSB2_ONAIR_CREATOR - bool "pvrusb2 driver support for OnAir Creator model" - depends on VIDEO_PVRUSB2 && EXPERIMENTAL - select VIDEO_SAA711X - select VIDEO_CS53L32A - ---help--- - - This option enables support for the OnAir Creator USB tuner - device. This is a hybrid device, however currently only - analog mode is supported. - - If you are in doubt, say Y. - -config VIDEO_PVRUSB2_ONAIR_USB2 - bool "pvrusb2 driver support for OnAir USB2 model" - depends on VIDEO_PVRUSB2 && EXPERIMENTAL - select VIDEO_SAA711X - select VIDEO_CS53L32A - ---help--- - - This option enables support for the OnAir USB2 tuner device - (also known as the Sasem tuner). This is a hybrid device, - however currently only analog mode is supported. - - If you are in doubt, say Y. - config VIDEO_PVRUSB2_SYSFS bool "pvrusb2 sysfs support (EXPERIMENTAL)" default y @@ -58,6 +35,25 @@ config VIDEO_PVRUSB2_SYSFS Note: This feature is experimental and subject to change. +config VIDEO_PVRUSB2_DVB + bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)" + default y + depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE + ---help--- + + This option enables a DVB interface for the pvrusb2 driver. + If your device does not support digital television, this + feature will have no affect on the driver's operation. + + If you are in doubt, say Y. + config VIDEO_PVRUSB2_DEBUGIFC bool "pvrusb2 debug interface" depends on VIDEO_PVRUSB2_SYSFS diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index 47284e558..4fda2de69 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -1,5 +1,6 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o +obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ @@ -9,6 +10,12 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + $(obj-pvrusb2-dvb-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.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 diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 3ecf38d40..65babb878 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -76,7 +75,7 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != 0) && + ((sp = routing_schemes + sid) != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { route.input = sp->def[hdw->input_val]; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h index 536339b68..ac54eed37 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index b1b4f3a12..9631ade1e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -1,5 +1,4 @@ /* - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -23,107 +22,245 @@ #include "pvrusb2-ioread.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#include <linux/wait.h> #include <linux/kthread.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/slab.h> -#include <asm/semaphore.h> #include "compat.h" +static struct pvr2_context *pvr2_context_exist_first; +static struct pvr2_context *pvr2_context_exist_last; +static struct pvr2_context *pvr2_context_notify_first; +static struct pvr2_context *pvr2_context_notify_last; +static DEFINE_MUTEX(pvr2_context_mutex); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); +static int pvr2_context_cleanup_flag; +static int pvr2_context_cleaned_flag; +static struct task_struct *pvr2_context_thread_ptr; + + +static void pvr2_context_set_notify(struct pvr2_context *mp, int fl) +{ + int signal_flag = 0; + mutex_lock(&pvr2_context_mutex); + if (fl) { + if (!mp->notify_flag) { + signal_flag = (pvr2_context_notify_first == NULL); + mp->notify_prev = pvr2_context_notify_last; + mp->notify_next = NULL; + pvr2_context_notify_last = mp; + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp; + } else { + pvr2_context_notify_first = mp; + } + mp->notify_flag = !0; + } + } else { + if (mp->notify_flag) { + mp->notify_flag = 0; + if (mp->notify_next) { + mp->notify_next->notify_prev = mp->notify_prev; + } else { + pvr2_context_notify_last = mp->notify_prev; + } + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp->notify_next; + } else { + pvr2_context_notify_first = mp->notify_next; + } + } + } + mutex_unlock(&pvr2_context_mutex); + if (signal_flag) wake_up(&pvr2_context_sync_data); +} + static void pvr2_context_destroy(struct pvr2_context *mp) { pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); if (mp->hdw) pvr2_hdw_destroy(mp->hdw); + pvr2_context_set_notify(mp, 0); + mutex_lock(&pvr2_context_mutex); + if (mp->exist_next) { + mp->exist_next->exist_prev = mp->exist_prev; + } else { + pvr2_context_exist_last = mp->exist_prev; + } + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp->exist_next; + } else { + pvr2_context_exist_first = mp->exist_next; + } + if (!pvr2_context_exist_first) { + /* Trigger wakeup on control thread in case it is waiting + for an exit condition. */ + wake_up(&pvr2_context_sync_data); + } + mutex_unlock(&pvr2_context_mutex); kfree(mp); } static void pvr2_context_notify(struct pvr2_context *mp) { - mp->notify_flag = !0; - wake_up(&mp->wait_data); + pvr2_context_set_notify(mp,!0); } -static int pvr2_context_thread(void *_mp) +static void pvr2_context_check(struct pvr2_context *mp) { - struct pvr2_channel *ch1,*ch2; - struct pvr2_context *mp = _mp; - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread start)",mp); - - /* Finish hardware initialization */ - if (pvr2_hdw_initialize(mp->hdw, - (void (*)(void *))pvr2_context_notify,mp)) { - mp->video_stream.stream = - pvr2_hdw_get_video_stream(mp->hdw); - /* Trigger interface initialization. By doing this here - initialization runs in our own safe and cozy thread - context. */ - if (mp->setup_func) mp->setup_func(mp); - } else { + struct pvr2_channel *ch1, *ch2; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (notify)", mp); + if (!mp->initialized_flag && !mp->disconnect_flag) { + mp->initialized_flag = !0; pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (thread skipping setup)",mp); - /* Even though initialization did not succeed, we're still - going to enter the wait loop anyway. We need to do this - in order to await the expected disconnect (which we will - detect in the normal course of operation). */ - } - - /* Now just issue callbacks whenever hardware state changes or if - there is a disconnect. If there is a disconnect and there are - no channels left, then there's no reason to stick around anymore - so we'll self-destruct - tearing down the rest of this driver - instance along the way. */ - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread enter loop)",mp); - while (!mp->disconnect_flag || mp->mc_first) { - if (mp->notify_flag) { - mp->notify_flag = 0; + "pvr2_context %p (initialize)", mp); + /* Finish hardware initialization */ + if (pvr2_hdw_initialize(mp->hdw, + (void (*)(void *))pvr2_context_notify, + mp)) { + mp->video_stream.stream = + pvr2_hdw_get_video_stream(mp->hdw); + /* Trigger interface initialization. By doing this + here initialization runs in our own safe and + cozy thread context. */ + if (mp->setup_func) mp->setup_func(mp); + } else { pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (thread notify)",mp); - for (ch1 = mp->mc_first; ch1; ch1 = ch2) { - ch2 = ch1->mc_next; - if (ch1->check_func) ch1->check_func(ch1); - } + "pvr2_context %p (thread skipping setup)", + mp); + /* Even though initialization did not succeed, + we're still going to continue anyway. We need + to do this in order to await the expected + disconnect (which we will detect in the normal + course of operation). */ } - wait_event_interruptible(mp->wait_data, mp->notify_flag); } - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread end)",mp); - pvr2_context_destroy(mp); + + for (ch1 = mp->mc_first; ch1; ch1 = ch2) { + ch2 = ch1->mc_next; + if (ch1->check_func) ch1->check_func(ch1); + } + + if (mp->disconnect_flag && !mp->mc_first) { + /* Go away... */ + pvr2_context_destroy(mp); + return; + } +} + + +static int pvr2_context_shutok(void) +{ + return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); +} + + +static int pvr2_context_thread_func(void *foo) +{ + struct pvr2_context *mp; + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start"); + + do { + while ((mp = pvr2_context_notify_first) != NULL) { + pvr2_context_set_notify(mp, 0); + pvr2_context_check(mp); + } + wait_event_interruptible( + pvr2_context_sync_data, + ((pvr2_context_notify_first != NULL) || + pvr2_context_shutok())); + } while (!pvr2_context_shutok()); + + pvr2_context_cleaned_flag = !0; + wake_up(&pvr2_context_cleanup_data); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up"); + + wait_event_interruptible( + pvr2_context_sync_data, + kthread_should_stop()); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); + return 0; } +int pvr2_context_global_init(void) +{ + pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, + NULL, + "pvrusb2-context"); + return (pvr2_context_thread_ptr ? 0 : -ENOMEM); +} + + +void pvr2_context_global_done(void) +{ + pvr2_context_cleanup_flag = !0; + wake_up(&pvr2_context_sync_data); + wait_event_interruptible( + pvr2_context_cleanup_data, + pvr2_context_cleaned_flag); + kthread_stop(pvr2_context_thread_ptr); +} + + struct pvr2_context *pvr2_context_create( struct usb_interface *intf, const struct usb_device_id *devid, void (*setup_func)(struct pvr2_context *)) { - struct task_struct *thread; struct pvr2_context *mp = NULL; mp = kzalloc(sizeof(*mp),GFP_KERNEL); if (!mp) goto done; pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); - init_waitqueue_head(&mp->wait_data); mp->setup_func = setup_func; mutex_init(&mp->mutex); + mutex_lock(&pvr2_context_mutex); + mp->exist_prev = pvr2_context_exist_last; + mp->exist_next = NULL; + pvr2_context_exist_last = mp; + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp; + } else { + pvr2_context_exist_first = mp; + } + mutex_unlock(&pvr2_context_mutex); mp->hdw = pvr2_hdw_create(intf,devid); if (!mp->hdw) { pvr2_context_destroy(mp); mp = NULL; goto done; } - thread = kthread_run(pvr2_context_thread, mp, "pvrusb2-context"); - if (!thread) { - pvr2_context_destroy(mp); - mp = NULL; - goto done; - } + pvr2_context_set_notify(mp, !0); done: return mp; } +static void pvr2_context_reset_input_limits(struct pvr2_context *mp) +{ + unsigned int tmsk,mmsk; + struct pvr2_channel *cp; + struct pvr2_hdw *hdw = mp->hdw; + mmsk = pvr2_hdw_get_input_available(hdw); + tmsk = mmsk; + for (cp = mp->mc_first; cp; cp = cp->mc_next) { + if (!cp->input_mask) continue; + tmsk &= cp->input_mask; + } + pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk); + pvr2_hdw_commit_ctl(hdw); +} + + static void pvr2_context_enter(struct pvr2_context *mp) { mutex_lock(&mp->mutex); @@ -179,7 +316,9 @@ void pvr2_channel_done(struct pvr2_channel *cp) { struct pvr2_context *mp = cp->mc_head; pvr2_context_enter(mp); + cp->input_mask = 0; pvr2_channel_disclaim_stream(cp); + pvr2_context_reset_input_limits(mp); if (cp->mc_next) { cp->mc_next->mc_prev = cp->mc_prev; } else { @@ -195,6 +334,57 @@ void pvr2_channel_done(struct pvr2_channel *cp) } +int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk) +{ + unsigned int tmsk,mmsk; + int ret = 0; + struct pvr2_channel *p2; + struct pvr2_hdw *hdw = cp->hdw; + + mmsk = pvr2_hdw_get_input_available(hdw); + cmsk &= mmsk; + if (cmsk == cp->input_mask) { + /* No change; nothing to do */ + return 0; + } + + pvr2_context_enter(cp->mc_head); + do { + if (!cmsk) { + cp->input_mask = 0; + pvr2_context_reset_input_limits(cp->mc_head); + break; + } + tmsk = mmsk; + for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) { + if (p2 == cp) continue; + if (!p2->input_mask) continue; + tmsk &= p2->input_mask; + } + if (!(tmsk & cmsk)) { + ret = -EPERM; + break; + } + tmsk &= cmsk; + if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) { + /* Internal failure changing allowed list; probably + should not happen, but react if it does. */ + break; + } + cp->input_mask = cmsk; + pvr2_hdw_commit_ctl(hdw); + } while (0); + pvr2_context_exit(cp->mc_head); + return ret; +} + + +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) +{ + return cp->input_mask; +} + + int pvr2_channel_claim_stream(struct pvr2_channel *cp, struct pvr2_context_stream *sp) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h index 1df66ac81..c7cbc28a8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -1,5 +1,4 @@ /* - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -44,6 +43,10 @@ struct pvr2_context_stream { struct pvr2_context { struct pvr2_channel *mc_first; struct pvr2_channel *mc_last; + struct pvr2_context *exist_next; + struct pvr2_context *exist_prev; + struct pvr2_context *notify_next; + struct pvr2_context *notify_prev; struct pvr2_hdw *hdw; struct pvr2_context_stream video_stream; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) @@ -52,10 +55,9 @@ struct pvr2_context { struct semaphore mutex; #endif int notify_flag; + int initialized_flag; int disconnect_flag; - wait_queue_head_t wait_data; - /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); @@ -67,6 +69,7 @@ struct pvr2_channel { struct pvr2_channel *mc_prev; struct pvr2_context_stream *stream; struct pvr2_hdw *hdw; + unsigned int input_mask; void (*check_func)(struct pvr2_channel *); }; @@ -77,11 +80,15 @@ void pvr2_context_disconnect(struct pvr2_context *); void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); void pvr2_channel_done(struct pvr2_channel *); +int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int); +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *); int pvr2_channel_claim_stream(struct pvr2_channel *, struct pvr2_context_stream *); struct pvr2_ioread *pvr2_channel_create_mpeg_stream( struct pvr2_context_stream *); +int pvr2_context_global_init(void); +void pvr2_context_global_done(void); #endif /* __PVRUSB2_CONTEXT_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 8b500528d..5c8942e93 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h index c1680053c..0371ae6e6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c64bd6078..2f051f797 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -85,7 +84,9 @@ static const struct routing_scheme_item routing_schemegv[] = { .vid = CX25840_COMPOSITE2, .aud = CX25840_AUDIO5, }, - [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + [PVR2_CVAL_INPUT_RADIO] = { + /* line-in is used for radio and composite. A GPIO is + used to switch between the two choices. */ .vid = CX25840_COMPOSITE1, .aud = CX25840_AUDIO_SERIAL, }, @@ -122,7 +123,7 @@ static void set_input(struct pvr2_v4l_cx2584x *ctxt) memset(&route,0,sizeof(route)); if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != 0) && + ((sp = routing_schemes + sid) != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { vid_input = sp->def[hdw->input_val].vid; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h index 54b2844e7..66abf77f5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h index 11537ddf8..be79249f8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h @@ -1,5 +1,4 @@ /* - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -54,6 +53,7 @@ extern int pvrusb2_debug; #define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */ #define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */ #define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */ +#define PVR2_TRACE_DVB_FEED (1 << 28) /* DVB transport feed debug */ #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index b0687430f..ca892fb78 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -164,6 +163,8 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, int ccnt; int ret; u32 gpio_dir,gpio_in,gpio_out; + struct pvr2_stream_stats stats; + struct pvr2_stream *sp; ret = pvr2_hdw_is_hsm(hdw); ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", @@ -182,6 +183,24 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, pvr2_hdw_get_streaming(hdw) ? "on" : "off"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + sp = pvr2_hdw_get_video_stream(hdw); + if (sp) { + pvr2_stream_get_stats(sp, &stats, 0); + ccnt = scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u\n", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + return bcnt; } @@ -220,6 +239,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, return pvr2_hdw_cmd_decoder_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"worker")) { return pvr2_hdw_untrip(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { + pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), + NULL, !0); + return 0; } return -EINVAL; } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h index 990b02d35..e24ff59f8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 3434b0e6d..5d036e7e3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2007 Mike Isely <isely@pobox.com> * @@ -32,7 +31,16 @@ pvr2_device_desc structures. /* This is needed in order to pull in tuner type ids... */ #include <linux/i2c.h> #include <media/tuner.h> - +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-hdw-internal.h" +#include "lgdt330x.h" +#include "s5h1409.h" +#include "s5h1411.h" +#include "tda10048.h" +#include "tda18271.h" +#include "tda8290.h" +#include "tuner-simple.h" +#endif /*------------------------------------------------------------------------*/ @@ -49,7 +57,7 @@ static const char *pvr2_fw1_names_29xxx[] = { }; static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model Category 29xxxx", + .description = "WinTV PVR USB2 Model Category 29xxx", .shortname = "29xxx", .client_modules.lst = pvr2_client_29xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), @@ -80,7 +88,7 @@ static const char *pvr2_fw1_names_24xxx[] = { }; static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model Category 24xxxx", + .description = "WinTV PVR USB2 Model Category 24xxx", .shortname = "24xxx", .client_modules.lst = pvr2_client_24xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), @@ -116,6 +124,7 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, @@ -143,10 +152,41 @@ static const struct pvr2_device_desc pvr2_device_gotview_2d = { -#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR /*------------------------------------------------------------------------*/ /* OnAir Creator */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .clock_polarity_flip = 1, +}; + +static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); + + return 0; +} + +static struct pvr2_dvb_props pvr2_onair_creator_fe_props = { + .frontend_attach = pvr2_lgdt3303_attach, + .tuner_attach = pvr2_lgh06xf_attach, +}; +#endif + static const char *pvr2_client_onair_creator[] = { "saa7115", "tuner", @@ -162,18 +202,51 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, -}; +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_creator_fe_props, #endif +}; -#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2 /*------------------------------------------------------------------------*/ /* OnAir USB 2.0 */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3302_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3302, +}; + +static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_PHILIPS_FCV1236D); + + return 0; +} + +static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { + .frontend_attach = pvr2_lgdt3302_attach, + .tuner_attach = pvr2_fcv1236d_attach, +}; +#endif + static const char *pvr2_client_onair_usb2[] = { "saa7115", "tuner", @@ -189,17 +262,64 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, -}; +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_usb2_fe_props, #endif +}; /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 73xxx */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct tda10048_config hauppauge_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_50, + .inversion = TDA10048_INVERSION_ON, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + +static struct tda18271_config hauppauge_tda18271_dvb_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_dvb_config); + + return 0; +} + +static struct pvr2_dvb_props pvr2_73xxx_dvb_props = { + .frontend_attach = pvr2_tda10048_attach, + .tuner_attach = pvr2_73xxx_tda18271_8295_attach, +}; +#endif + static const char *pvr2_client_73xxx[] = { "cx25840", "tuner", @@ -210,7 +330,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV PVR USB2 Model Category 73xxxx", + .description = "WinTV PVR USB2 Model Category 73xxx", .shortname = "73xxx", .client_modules.lst = pvr2_client_73xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx), @@ -224,6 +344,9 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_73xxx_dvb_props, +#endif }; @@ -231,6 +354,80 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 75xxx */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct s5h1409_config pvr2_s5h1409_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_PARALLEL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .qam_if = 4000, + .inversion = S5H1409_INVERSION_ON, + .status_mode = S5H1409_DEMODLOCKING, +}; + +static struct s5h1411_config pvr2_s5h1411_config = { + .output_mode = S5H1411_PARALLEL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .vsb_if = S5H1411_IF_44000, + .qam_if = S5H1411_IF_4000, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config hauppauge_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + +static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_config); + + return 0; +} + +static struct pvr2_dvb_props pvr2_750xx_dvb_props = { + .frontend_attach = pvr2_s5h1409_attach, + .tuner_attach = pvr2_tda18271_8295_attach, +}; + +static struct pvr2_dvb_props pvr2_751xx_dvb_props = { + .frontend_attach = pvr2_s5h1411_attach, + .tuner_attach = pvr2_tda18271_8295_attach, +}; +#endif + static const char *pvr2_client_75xxx[] = { "cx25840", "tuner", @@ -240,9 +437,9 @@ static const char *pvr2_fw1_names_75xxx[] = { "v4l-pvrusb2-73xxx-01.fw", }; -static const struct pvr2_device_desc pvr2_device_75xxx = { - .description = "WinTV PVR USB2 Model Category 75xxxx", - .shortname = "75xxx", +static const struct pvr2_device_desc pvr2_device_750xx = { + .description = "WinTV PVR USB2 Model Category 750xx", + .shortname = "750xx", .client_modules.lst = pvr2_client_75xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, @@ -256,6 +453,30 @@ static const struct pvr2_device_desc pvr2_device_75xxx = { .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_750xx_dvb_props, +#endif +}; + +static const struct pvr2_device_desc pvr2_device_751xx = { + .description = "WinTV PVR USB2 Model Category 751xx", + .shortname = "751xx", + .client_modules.lst = pvr2_client_75xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .fx2_firmware.lst = pvr2_fw1_names_75xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_751xx_dvb_props, +#endif }; @@ -271,20 +492,16 @@ struct usb_device_id pvr2_device_table[] = { .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, { USB_DEVICE(0x1164, 0x0602), .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d}, -#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR { USB_DEVICE(0x11ba, 0x1003), .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, -#endif -#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2 { USB_DEVICE(0x11ba, 0x1001), .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2}, -#endif { USB_DEVICE(0x2040, 0x7300), .driver_info = (kernel_ulong_t)&pvr2_device_73xxx}, { USB_DEVICE(0x2040, 0x7500), - .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, { USB_DEVICE(0x2040, 0x7501), - .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, { } }; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index fb5f5d17e..e23ce1d2e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -23,6 +22,9 @@ #include <linux/mod_devicetable.h> #include <linux/videodev2.h> +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-dvb.h" +#endif /* @@ -65,6 +67,11 @@ struct pvr2_device_desc { was initialized from internal ROM. */ struct pvr2_string_table fx2_firmware; +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* callback functions to handle attachment of digital tuner & demod */ + struct pvr2_dvb_props *dvb_props; + +#endif /* Initial standard bits to use for this device, if not zero. Anything set here is also implied as an available standard. Note: This is ignored if overridden on the module load line via @@ -96,21 +103,28 @@ struct pvr2_device_desc { unsigned char digital_control_scheme; /* If set, we don't bother trying to load cx23416 firmware. */ - int flag_skip_cx23416_firmware:1; + unsigned int flag_skip_cx23416_firmware:1; + + /* If set, the encoder must be healthy in order for digital mode to + work (otherwise we assume that digital streaming will work even + if we fail to locate firmware for the encoder). If the device + doesn't support digital streaming then this flag has no + effect. */ + unsigned int flag_digital_requires_cx23416:1; /* Device has a hauppauge eeprom which we can interrogate. */ - int flag_has_hauppauge_rom:1; + unsigned int flag_has_hauppauge_rom:1; /* Device does not require a powerup command to be issued. */ - int flag_no_powerup:1; + unsigned int flag_no_powerup:1; /* Device has a cx25840 - this enables special additional logic to handle it. */ - int flag_has_cx25840:1; + unsigned int flag_has_cx25840:1; /* Device has a wm8775 - this enables special additional logic to ensure that it is found. */ - int flag_has_wm8775:1; + unsigned int flag_has_wm8775:1; /* Device has IR hardware that can be faked into looking like a normal Hauppauge i2c IR receiver. This is currently very @@ -120,15 +134,15 @@ struct pvr2_device_desc { to virtualize the presence of the non-existant IR receiver chip and implement the virtual receiver in terms of appropriate FX2 commands. */ - int flag_has_hauppauge_custom_ir:1; + unsigned int flag_has_hauppauge_custom_ir:1; /* These bits define which kinds of sources the device can handle. Note: Digital tuner presence is inferred by the digital_control_scheme enumeration. */ - int flag_has_fmradio:1; /* Has FM radio receiver */ - int flag_has_analogtuner:1; /* Has analog tuner */ - int flag_has_composite:1; /* Has composite input */ - int flag_has_svideo:1; /* Has s-video input */ + unsigned int flag_has_fmradio:1; /* Has FM radio receiver */ + unsigned int flag_has_analogtuner:1; /* Has analog tuner */ + unsigned int flag_has_composite:1; /* Has composite input */ + unsigned int flag_has_svideo:1; /* Has s-video input */ }; extern struct usb_device_id pvr2_device_table[]; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c new file mode 100644 index 000000000..a317347ad --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -0,0 +1,438 @@ +/* + * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. + * + * Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kthread.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +#include <linux/suspend.h> +#else +#include <linux/freezer.h> +#endif +#include "compat.h" +#include "dvbdev.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-io.h" +#include "pvrusb2-dvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) +{ + int ret; + unsigned int count; + struct pvr2_buffer *bp; + struct pvr2_stream *stream; + + pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started"); + set_freezable(); + + stream = adap->channel.stream->stream; + + for (;;) { + if (kthread_should_stop()) break; + + /* Not sure about this... */ + try_to_freeze(); + + bp = pvr2_stream_get_ready_buffer(stream); + if (bp != NULL) { + count = pvr2_buffer_get_count(bp); + if (count) { + dvb_dmx_swfilter( + &adap->demux, + adap->buffer_storage[ + pvr2_buffer_get_id(bp)], + count); + } else { + ret = pvr2_buffer_get_status(bp); + if (ret < 0) break; + } + ret = pvr2_buffer_queue(bp); + if (ret < 0) break; + + /* Since we know we did something to a buffer, + just go back and try again. No point in + blocking unless we really ran out of + buffers to process. */ + continue; + } + + + /* Wait until more buffers become available or we're + told not to wait any longer. */ + ret = wait_event_interruptible( + adap->buffer_wait_data, + (pvr2_stream_get_ready_count(stream) > 0) || + kthread_should_stop()); + if (ret < 0) break; + } + + /* If we get here and ret is < 0, then an error has occurred. + Probably would be a good idea to communicate that to DVB core... */ + + pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped"); + + return 0; +} + +static int pvr2_dvb_feed_thread(void *data) +{ + int stat = pvr2_dvb_feed_func(data); + /* from videobuf-dvb.c: */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return stat; +} + +static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) +{ + wake_up(&adap->buffer_wait_data); +} + +static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) +{ + unsigned int idx; + struct pvr2_stream *stream; + + if (adap->thread) { + kthread_stop(adap->thread); + adap->thread = NULL; + } + + if (adap->channel.stream) { + stream = adap->channel.stream->stream; + } else { + stream = NULL; + } + if (stream) { + pvr2_hdw_set_streaming(adap->channel.hdw, 0); + pvr2_stream_set_callback(stream, NULL, NULL); + pvr2_stream_kill(stream); + pvr2_stream_set_buffer_count(stream, 0); + pvr2_channel_claim_stream(&adap->channel, NULL); + } + + if (adap->stream_run) { + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + if (!(adap->buffer_storage[idx])) continue; + kfree(adap->buffer_storage[idx]); + adap->buffer_storage[idx] = NULL; + } + adap->stream_run = 0; + } +} + +static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_context *pvr = adap->channel.mc_head; + unsigned int idx; + int ret; + struct pvr2_buffer *bp; + struct pvr2_stream *stream = NULL; + + if (adap->stream_run) return -EIO; + + ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); + /* somebody else already has the stream */ + if (ret < 0) return ret; + + stream = adap->channel.stream->stream; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, + GFP_KERNEL); + if (!(adap->buffer_storage[idx])) return -ENOMEM; + } + + pvr2_stream_set_callback(pvr->video_stream.stream, + (pvr2_stream_callback) pvr2_dvb_notify, adap); + + ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); + if (ret < 0) return ret; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + bp = pvr2_stream_get_buffer(stream, idx); + pvr2_buffer_set_buffer(bp, + adap->buffer_storage[idx], + PVR2_DVB_BUFFER_SIZE); + } + + ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); + if (ret < 0) return ret; + + while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) { + ret = pvr2_buffer_queue(bp); + if (ret < 0) return ret; + } + + adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); + + if (IS_ERR(adap->thread)) { + ret = PTR_ERR(adap->thread); + adap->thread = NULL; + return ret; + } + + adap->stream_run = !0; + + return 0; +} + +static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) +{ + int ret = pvr2_dvb_stream_do_start(adap); + if (ret < 0) pvr2_dvb_stream_end(adap); + return ret; +} + +static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; + int ret = 0; + + if (adap == NULL) return -ENODEV; + + mutex_lock(&adap->lock); + do { + if (onoff) { + if (!adap->feedcount) { + pvr2_trace(PVR2_TRACE_DVB_FEED, + "start feeding demux"); + ret = pvr2_dvb_stream_start(adap); + if (ret < 0) break; + } + (adap->feedcount)++; + } else if (adap->feedcount > 0) { + (adap->feedcount)--; + if (!adap->feedcount) { + pvr2_trace(PVR2_TRACE_DVB_FEED, + "stop feeding demux"); + pvr2_dvb_stream_end(adap); + } + } + } while (0); + mutex_unlock(&adap->lock); + + return ret; +} + +static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); +} + +static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); +} + +static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct pvr2_dvb_adapter *adap = fe->dvb->priv; + return pvr2_channel_limit_inputs( + &adap->channel, + (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); +} + +static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) +{ + int ret; + + ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", + THIS_MODULE/*&hdw->usb_dev->owner*/, + &adap->channel.hdw->usb_dev->dev, + adapter_nr); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "dvb_register_adapter failed: error %d", ret); + goto err; + } + adap->dvb_adap.priv = adap; + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + adap->demux.priv = adap; + adap->demux.filternum = 256; + adap->demux.feednum = 256; + adap->demux.start_feed = pvr2_dvb_start_feed; + adap->demux.stop_feed = pvr2_dvb_stop_feed; + adap->demux.write_to_decoder = NULL; + + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "dvb_dmx_init failed: error %d", ret); + goto err_dmx; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "dvb_dmxdev_init failed: error %d", ret); + goto err_dmx_dev; + } + + dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); + + return 0; + +err_dmx_dev: + dvb_dmx_release(&adap->demux); +err_dmx: + dvb_unregister_adapter(&adap->dvb_adap); +err: + return ret; +} + +static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) +{ + pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices"); + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + return 0; +} + +static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_hdw *hdw = adap->channel.hdw; + struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; + int ret = 0; + + if (dvb_props == NULL) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!"); + return -EINVAL; + } + + ret = pvr2_channel_limit_inputs( + &adap->channel, + (1 << PVR2_CVAL_INPUT_DTV)); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "failed to grab control of dtv input (code=%d)", + ret); + return ret; + } + + if (dvb_props->frontend_attach == NULL) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "frontend_attach not defined!"); + ret = -EINVAL; + goto done; + } + + if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "frontend registration failed!"); + dvb_frontend_detach(adap->fe); + adap->fe = NULL; + ret = -ENODEV; + goto done; + } + + if (dvb_props->tuner_attach) + dvb_props->tuner_attach(adap); + + if (adap->fe->ops.analog_ops.standby) + adap->fe->ops.analog_ops.standby(adap->fe); + + /* Ensure all frontends negotiate bus access */ + adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; + + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "no frontend was attached!"); + ret = -ENODEV; + return ret; + } + + done: + pvr2_channel_limit_inputs(&adap->channel, 0); + return ret; +} + +static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) +{ + if (adap->fe != NULL) { + dvb_unregister_frontend(adap->fe); + dvb_frontend_detach(adap->fe); + } + return 0; +} + +static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) +{ + pvr2_dvb_stream_end(adap); + pvr2_dvb_frontend_exit(adap); + pvr2_dvb_adapter_exit(adap); + pvr2_channel_done(&adap->channel); + kfree(adap); +} + +static void pvr2_dvb_internal_check(struct pvr2_channel *chp) +{ + struct pvr2_dvb_adapter *adap; + adap = container_of(chp, struct pvr2_dvb_adapter, channel); + if (!adap->channel.mc_head->disconnect_flag) return; + pvr2_dvb_destroy(adap); +} + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) +{ + int ret = 0; + struct pvr2_dvb_adapter *adap; + if (!pvr->hdw->hdw_desc->dvb_props) { + /* Device lacks a digital interface so don't set up + the DVB side of the driver either. For now. */ + return NULL; + } + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) return adap; + pvr2_channel_init(&adap->channel, pvr); + adap->channel.check_func = pvr2_dvb_internal_check; + init_waitqueue_head(&adap->buffer_wait_data); + mutex_init(&adap->lock); + ret = pvr2_dvb_adapter_init(adap); + if (ret < 0) goto fail1; + ret = pvr2_dvb_frontend_init(adap); + if (ret < 0) goto fail2; + return adap; + +fail2: + pvr2_dvb_adapter_exit(adap); +fail1: + pvr2_channel_done(&adap->channel); + return NULL; +} + diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.h new file mode 100644 index 000000000..c13ecfb13 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.h @@ -0,0 +1,45 @@ +#ifndef __PVRUSB2_DVB_H__ +#define __PVRUSB2_DVB_H__ + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" +#include "pvrusb2-context.h" + +#define PVR2_DVB_BUFFER_COUNT 32 +#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000) + +struct pvr2_dvb_adapter { + struct pvr2_channel channel; + + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend *fe; + + int feedcount; + int max_feed_count; + + struct task_struct *thread; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) + struct mutex lock; +#else + struct semaphore lock; +#endif + + unsigned int stream_run:1; + + wait_queue_head_t buffer_wait_data; + char *buffer_storage[PVR2_DVB_BUFFER_COUNT]; +}; + +struct pvr2_dvb_props { + int (*frontend_attach) (struct pvr2_dvb_adapter *); + int (*tuner_attach) (struct pvr2_dvb_adapter *); +}; + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr); + +#endif /* __PVRUSB2_DVB_H__ */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index b831559bd..349618395 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h index 84242975d..cca3216f9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 70a2bc251..da5b5a7e9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -300,11 +299,20 @@ static int pvr2_encoder_cmd(void *ctxt, ret = -EBUSY; } if (ret) { + del_timer_sync(&hdw->encoder_run_timer); hdw->state_encoder_ok = 0; pvr2_trace(PVR2_TRACE_STBITS, "State bit %s <-- %s", "state_encoder_ok", (hdw->state_encoder_ok ? "true" : "false")); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_runok", + (hdw->state_encoder_runok ? + "true" : "false")); + } pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h index 54caf2e3c..232fefbcd 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h index a866c9492..b58369e7f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org> * @@ -22,41 +21,41 @@ #ifndef _PVRUSB2_FX2_CMD_H_ #define _PVRUSB2_FX2_CMD_H_ -#define FX2CMD_MEM_WRITE_DWORD 0x01 -#define FX2CMD_MEM_READ_DWORD 0x02 +#define FX2CMD_MEM_WRITE_DWORD 0x01u +#define FX2CMD_MEM_READ_DWORD 0x02u -#define FX2CMD_MEM_READ_64BYTES 0x28 +#define FX2CMD_MEM_READ_64BYTES 0x28u -#define FX2CMD_REG_WRITE 0x04 -#define FX2CMD_REG_READ 0x05 -#define FX2CMD_MEMSEL 0x06 +#define FX2CMD_REG_WRITE 0x04u +#define FX2CMD_REG_READ 0x05u +#define FX2CMD_MEMSEL 0x06u -#define FX2CMD_I2C_WRITE 0x08 -#define FX2CMD_I2C_READ 0x09 +#define FX2CMD_I2C_WRITE 0x08u +#define FX2CMD_I2C_READ 0x09u -#define FX2CMD_GET_USB_SPEED 0x0b +#define FX2CMD_GET_USB_SPEED 0x0bu -#define FX2CMD_STREAMING_ON 0x36 -#define FX2CMD_STREAMING_OFF 0x37 +#define FX2CMD_STREAMING_ON 0x36u +#define FX2CMD_STREAMING_OFF 0x37u -#define FX2CMD_FWPOST1 0x52 +#define FX2CMD_FWPOST1 0x52u -#define FX2CMD_POWER_OFF 0xdc -#define FX2CMD_POWER_ON 0xde +#define FX2CMD_POWER_OFF 0xdcu +#define FX2CMD_POWER_ON 0xdeu -#define FX2CMD_DEEP_RESET 0xdd +#define FX2CMD_DEEP_RESET 0xddu -#define FX2CMD_GET_EEPROM_ADDR 0xeb -#define FX2CMD_GET_IR_CODE 0xec +#define FX2CMD_GET_EEPROM_ADDR 0xebu +#define FX2CMD_GET_IR_CODE 0xecu -#define FX2CMD_HCW_DEMOD_RESETIN 0xf0 -#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1 -#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2 +#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u +#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u +#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u -#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0 -#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1 -#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2 -#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3 +#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u +#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u +#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u +#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u #endif /* _PVRUSB2_FX2_CMD_H_ */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 631973652..f75dd1613 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -254,6 +253,7 @@ struct pvr2_hdw { int state_encoder_run; /* Encoder is running */ int state_encoder_config; /* Encoder is configured */ int state_encoder_waitok; /* Encoder pre-wait done */ + int state_encoder_runok; /* Encoder has run for >= .25 sec */ int state_decoder_run; /* Decoder is running */ int state_usbstream_run; /* FX2 is streaming */ int state_decoder_quiescent; /* Decoder idle for > 50msec */ @@ -283,6 +283,9 @@ struct pvr2_hdw { /* Timer for measuring encoder pre-wait time */ struct timer_list encoder_wait_timer; + /* Timer for measuring encoder minimum run time */ + struct timer_list encoder_run_timer; + /* Place to block while waiting for state changes */ wait_queue_head_t state_wait_data; @@ -348,8 +351,10 @@ struct pvr2_hdw { int v4l_minor_number_vbi; int v4l_minor_number_radio; - /* Bit mask of PVR2_CVAL_INPUT choices which are valid */ + /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ unsigned int input_avail_mask; + /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */ + unsigned int input_allowed_mask; /* Location of eeprom or a negative number if none */ int eeprom_addr; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8356bc0e4..f78bc351f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -25,7 +24,6 @@ #include <linux/firmware.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> -#include <asm/semaphore.h> #include "pvrusb2.h" #include "pvrusb2-std.h" #include "pvrusb2-util.h" @@ -42,6 +40,23 @@ #define TV_MIN_FREQ 55250000L #define TV_MAX_FREQ 850000000L +/* This defines a minimum interval that the decoder must remain quiet + before we are allowed to start it running. */ +#define TIME_MSEC_DECODER_WAIT 50 + +/* This defines a minimum interval that the encoder must remain quiet + before we are allowed to configure it. I had this originally set to + 50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that + things work better when it's set to 100msec. */ +#define TIME_MSEC_ENCODER_WAIT 100 + +/* This defines the minimum interval that the encoder must successfully run + before we consider that the encoder has run at least once since its + firmware has been loaded. This measurement is in important for cases + where we can't do something until we know that the encoder has been run + at least once. */ +#define TIME_MSEC_ENCODER_OK 250 + static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; static DEFINE_MUTEX(pvr2_unit_mtx); @@ -69,6 +84,16 @@ MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); +/* US Broadcast channel 7 (175.25 MHz) */ +static int default_tv_freq = 175250000L; +/* 104.3 MHz, a usable FM station for my area */ +static int default_radio_freq = 104300000L; + +module_param_named(tv_freq, default_tv_freq, int, 0444); +MODULE_PARM_DESC(tv_freq, "specify initial television frequency"); +module_param_named(radio_freq, default_radio_freq, int, 0444); +MODULE_PARM_DESC(radio_freq, "specify initial radio frequency"); + #define PVR2_CTL_WRITE_ENDPOINT 0x01 #define PVR2_CTL_READ_ENDPOINT 0x81 @@ -218,6 +243,40 @@ static const char *pvr2_state_names[] = { }; +struct pvr2_fx2cmd_descdef { + unsigned char id; + unsigned char *desc; +}; + +static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { + {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, + {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, + {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, + {FX2CMD_REG_WRITE, "write encoder register"}, + {FX2CMD_REG_READ, "read encoder register"}, + {FX2CMD_MEMSEL, "encoder memsel"}, + {FX2CMD_I2C_WRITE, "i2c write"}, + {FX2CMD_I2C_READ, "i2c read"}, + {FX2CMD_GET_USB_SPEED, "get USB speed"}, + {FX2CMD_STREAMING_ON, "stream on"}, + {FX2CMD_STREAMING_OFF, "stream off"}, + {FX2CMD_FWPOST1, "fwpost1"}, + {FX2CMD_POWER_OFF, "power off"}, + {FX2CMD_POWER_ON, "power on"}, + {FX2CMD_DEEP_RESET, "deep reset"}, + {FX2CMD_GET_EEPROM_ADDR, "get rom addr"}, + {FX2CMD_GET_IR_CODE, "get IR code"}, + {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"}, + {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"}, + {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"}, + {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"}, + {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, + {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, + {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, +}; + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); static void pvr2_hdw_state_sched(struct pvr2_hdw *); static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); @@ -233,6 +292,8 @@ static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); +static void pvr2_hdw_encoder_run_timeout(unsigned long); +static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, @@ -371,30 +432,12 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) { - return ((1 << v) & cptr->hdw->input_avail_mask) != 0; + return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; } static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - - if (hdw->input_val != v) { - hdw->input_val = v; - hdw->input_dirty = !0; - } - - /* Handle side effects - if we switch to a mode that needs the RF - tuner, then select the right frequency choice as well and mark - it dirty. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - hdw->freqSelector = 0; - hdw->freqDirty = !0; - } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || - (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { - hdw->freqSelector = 1; - hdw->freqDirty = !0; - } - return 0; + return pvr2_hdw_set_input(cptr->hdw,v); } static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) @@ -991,7 +1034,7 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) /* Set the currently tuned frequency and account for all possible driver-core side effects of this action. */ -void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) { if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { if (hdw->freqSelector) { @@ -1213,6 +1256,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; + /* Encoder is about to be reset so note that as far as we're + concerned now, the encoder has never been run. */ + del_timer_sync(&hdw->encoder_run_timer); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + } + /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ @@ -1230,19 +1281,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_FWPOST1; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload prep failed, ret=%d",ret); release_firmware(fw_entry); - return ret; + goto done; } /* Now send firmware */ @@ -1255,7 +1301,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) " must be a multiple of %zu bytes", fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); - return -1; + ret = -EINVAL; + goto done; } fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); @@ -1263,7 +1310,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) release_firmware(fw_entry); pvr2_trace(PVR2_TRACE_ERROR_LEGS, "failed to allocate memory for firmware2 upload"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); @@ -1294,23 +1342,27 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload transfer failure"); - return ret; + goto done; } /* Finish upload */ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); } + + done: + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } return ret; } @@ -1382,11 +1434,13 @@ int pvr2_hdw_untrip(struct pvr2_hdw *hdw) } +#if 0 const char *pvr2_hdw_get_state_name(unsigned int id) { if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL; return pvr2_state_names[id]; } +#endif /* 0 */ int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) @@ -1690,10 +1744,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) are, but I set them to something usable in the Chicago area just to make driver testing a little easier. */ - /* US Broadcast channel 7 (175.25 MHz) */ - hdw->freqValTelevision = 175250000L; - /* 104.3 MHz, a usable FM station for my area */ - hdw->freqValRadio = 104300000L; + hdw->freqValTelevision = default_tv_freq; + hdw->freqValRadio = default_radio_freq; // Do not use pvr2_reset_ctl_endpoints() here. It is not // thread-safe against the normal pvr2_send_request() mechanism. @@ -1730,6 +1782,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } + pvr2_hdw_commit_setup(hdw); hdw->vid_stream = pvr2_stream_create(); @@ -1831,10 +1890,19 @@ int pvr2_hdw_initialize(struct pvr2_hdw *hdw, void *callback_data) { LOCK_TAKE(hdw->big_lock); do { + if (hdw->flag_disconnected) { + /* Handle a race here: If we're already + disconnected by this point, then give up. If we + get past this then we'll remain connected for + the duration of initialization since the entire + initialization sequence is now protected by the + big_lock. */ + break; + } hdw->state_data = callback_data; hdw->state_func = callback_func; + pvr2_hdw_setup(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - pvr2_hdw_setup(hdw); return hdw->flag_init_ok; } @@ -1868,6 +1936,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->encoder_wait_timer.data = (unsigned long)hdw; hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + init_timer(&hdw->encoder_run_timer); + hdw->encoder_run_timer.data = (unsigned long)hdw; + hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; + hdw->master_state = PVR2_STATE_DEAD; init_waitqueue_head(&hdw->state_wait_data); @@ -1885,6 +1957,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; hdw->input_avail_mask = m; + hdw->input_allowed_mask = hdw->input_avail_mask; /* If not a hybrid device, pathway_state never changes. So initialize it here to what it should forever be. */ @@ -2075,6 +2148,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, fail: if (hdw) { del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { flush_workqueue(hdw->workqueue); @@ -2137,6 +2211,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) hdw->workqueue = NULL; } del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { kfree(hdw->fw_buffer); @@ -2419,7 +2494,7 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } - if (hdw->input_dirty && + if (hdw->input_dirty && hdw->state_pathway_ok && (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != hdw->pathway_state)) { @@ -2484,6 +2559,20 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->active_stream_type = hdw->desired_stream_type; } + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + u32 b; + /* Handle GOTVIEW audio switching */ + pvr2_hdw_gpio_get_out(hdw,&b); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + /* Set GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0); + } else { + /* Clear GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0); + } + } + /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); @@ -3185,6 +3274,67 @@ int pvr2_send_request(struct pvr2_hdw *hdw, read_data,read_len); } + +static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode) +{ + int ret; + unsigned int cnt = 1; + unsigned int args = 0; + LOCK_TAKE(hdw->ctl_lock); + hdw->cmd_buffer[0] = cmdcode & 0xffu; + args = (cmdcode >> 8) & 0xffu; + args = (args > 2) ? 2 : args; + if (args) { + cnt += args; + hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu; + if (args > 1) { + hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu; + } + } + if (pvrusb2_debug & PVR2_TRACE_INIT) { + unsigned int idx; + unsigned int ccnt,bcnt; + char tbuf[50]; + cmdcode &= 0xffu; + bcnt = 0; + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + "Sending FX2 command 0x%x",cmdcode); + bcnt += ccnt; + for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) { + if (pvr2_fx2cmd_desc[idx].id == cmdcode) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " \"%s\"", + pvr2_fx2cmd_desc[idx].desc); + bcnt += ccnt; + break; + } + } + if (args) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " (%u",hdw->cmd_buffer[1]); + bcnt += ccnt; + if (args > 1) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ",%u",hdw->cmd_buffer[2]); + bcnt += ccnt; + } + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ")"); + bcnt += ccnt; + } + pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf); + } + ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0); + LOCK_GIVE(hdw->ctl_lock); + return ret; +} + + int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) { int ret; @@ -3292,40 +3442,19 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); - hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET); } -static int pvr2_hdw_cmd_power_ctrl(struct pvr2_hdw *hdw, int onoff) -{ - int status; - LOCK_TAKE(hdw->ctl_lock); do { - if (onoff) { - pvr2_trace(PVR2_TRACE_INIT, "Requesting powerup"); - hdw->cmd_buffer[0] = FX2CMD_POWER_ON; - } else { - pvr2_trace(PVR2_TRACE_INIT, "Requesting powerdown"); - hdw->cmd_buffer[0] = FX2CMD_POWER_OFF; - } - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; -} - int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { - return pvr2_hdw_cmd_power_ctrl(hdw, 1); + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON); } + int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) { - return pvr2_hdw_cmd_power_ctrl(hdw, 0); + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); } @@ -3352,55 +3481,29 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) { - int status; - - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing fe demod wake command (%s)", - (onoff ? "on" : "off")); - hdw->flag_ok = !0; - hdw->cmd_buffer[0] = FX2CMD_HCW_DEMOD_RESETIN; - hdw->cmd_buffer[1] = onoff; - status = pvr2_send_request(hdw, hdw->cmd_buffer, 2, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - - return status; + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESETIN | + (1 << 8) | + ((onoff ? 1 : 0) << 16)); } static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) { - int status; - - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing fe power command to CPLD (%s)", - (onoff ? "on" : "off")); - hdw->flag_ok = !0; - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_ONAIR_DTV_POWER_ON : - FX2CMD_ONAIR_DTV_POWER_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - - return status; + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF)); } static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, int onoff) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing onair digital setup command (%s)", - (onoff ? "on" : "off")); - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_ONAIR_DTV_STREAMING_ON : - FX2CMD_ONAIR_DTV_STREAMING_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF)); } @@ -3430,9 +3533,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) /* Supposedly we should always have the power on whether in digital or analog mode. But for now do what appears to work... */ - if (digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,!0); - pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,digitalFl); - if (!digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,0); + pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl); break; default: break; } @@ -3442,7 +3543,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) } -void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) +static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) { /* change some GPIO data * @@ -3490,24 +3591,43 @@ static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { - int status,cc; - if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && - hdw->hdw_desc->digital_control_scheme == - PVR2_DIGITAL_SCHEME_HAUPPAUGE) { - cc = (runFl ? - FX2CMD_HCW_DTV_STREAMING_ON : - FX2CMD_HCW_DTV_STREAMING_OFF); - } else { - cc = (runFl ? - FX2CMD_STREAMING_ON : - FX2CMD_STREAMING_OFF); + int ret; + + /* If we're in analog mode, then just issue the usual analog + command. */ + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + /*Note: Not reached */ } - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = cc; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) { + /* Whoops, we don't know what mode we're in... */ + return -EINVAL; + } + + /* To get here we have to be in digital mode. The mechanism here + is unfortunately different for different vendors. So we switch + on the device's digital scheme attribute in order to figure out + what to do. */ + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF)); + case PVR2_DIGITAL_SCHEME_ONAIR: + ret = pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + if (ret) return ret; + return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl); + default: + return -EINVAL; + } } @@ -3538,7 +3658,12 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) if (hdw->state_encoder_config) return 0; if (hdw->state_decoder_run) return 0; if (hdw->state_usbstream_run) return 0; - if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) { + if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0; + } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) { + return 0; + } + if (pvr2_upload_firmware2(hdw) < 0) { hdw->flag_tripped = !0; trace_stbit("flag_tripped",hdw->flag_tripped); @@ -3598,7 +3723,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) the encoder. */ if (!hdw->state_encoder_waitok) { hdw->encoder_wait_timer.expires = - jiffies + (HZ*50/1000); + jiffies + + (HZ * TIME_MSEC_ENCODER_WAIT + / 1000); add_timer(&hdw->encoder_wait_timer); } } @@ -3615,23 +3742,116 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) } +/* Return true if the encoder should not be running. */ +static int state_check_disable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Encoder isn't healthy at the moment, so stop it. */ + return !0; + } + if (!hdw->state_pathway_ok) { + /* Mode is not understood at the moment (i.e. it wants to + change), so encoder must be stopped. */ + return !0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (!hdw->state_decoder_run) { + /* We're in analog mode and the decoder is not + running; thus the encoder should be stopped as + well. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if (hdw->state_encoder_runok) { + /* This is a funny case. We're in digital mode so + really the encoder should be stopped. However + if it really is running, only kill it after + runok has been set. This gives a chance for the + onair quirk to function (encoder must run + briefly first, at least once, before onair + digital streaming can work). */ + return !0; + } + break; + default: + /* Unknown mode; so encoder should be stopped. */ + return !0; + } + + /* If we get here, we haven't found a reason to stop the + encoder. */ + return 0; +} + + +/* Return true if the encoder should be running. */ +static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Don't run the encoder if it isn't healthy... */ + return 0; + } + if (!hdw->state_pathway_ok) { + /* Don't run the encoder if we don't (yet) know what mode + we need to be in... */ + return 0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (hdw->state_decoder_run) { + /* In analog mode, if the decoder is running, then + run the encoder. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if ((hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) && + !hdw->state_encoder_runok) { + /* This is a quirk. OnAir hardware won't stream + digital until the encoder has been run at least + once, for a minimal period of time (empiricially + measured to be 1/4 second). So if we're on + OnAir hardware and the encoder has never been + run at all, then start the encoder. Normal + state machine logic in the driver will + automatically handle the remaining bits. */ + return !0; + } + break; + default: + /* For completeness (unknown mode; encoder won't run ever) */ + break; + } + /* If we get here, then we haven't found any reason to run the + encoder, so don't run it. */ + return 0; +} + + /* Evaluate whether or not state_encoder_run can change */ static int state_eval_encoder_run(struct pvr2_hdw *hdw) { if (hdw->state_encoder_run) { + if (!state_check_disable_encoder_run(hdw)) return 0; if (hdw->state_encoder_ok) { - if (hdw->state_decoder_run && - hdw->state_pathway_ok) return 0; + del_timer_sync(&hdw->encoder_run_timer); if (pvr2_encoder_stop(hdw) < 0) return !0; } hdw->state_encoder_run = 0; } else { - if (!hdw->state_encoder_ok) return 0; - if (!hdw->state_decoder_run) return 0; - if (!hdw->state_pathway_ok) return 0; - if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; + if (!state_check_enable_encoder_run(hdw)) return 0; if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; + if (!hdw->state_encoder_runok) { + hdw->encoder_run_timer.expires = + jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000); + add_timer(&hdw->encoder_run_timer); + } } trace_stbit("state_encoder_run",hdw->state_encoder_run); return !0; @@ -3660,6 +3880,19 @@ static void pvr2_hdw_encoder_wait_timeout(unsigned long data) } +/* Timeout function for encoder run timer. */ +static void pvr2_hdw_encoder_run_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + if (!hdw->state_encoder_runok) { + hdw->state_encoder_runok = !0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); + } +} + + /* Evaluate whether or not state_decoder_run can change */ static int state_eval_decoder_run(struct pvr2_hdw *hdw) { @@ -3691,7 +3924,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) but before we did the pending check. */ if (!hdw->state_decoder_quiescent) { hdw->quiescent_timer.expires = - jiffies + (HZ*50/1000); + jiffies + + (HZ * TIME_MSEC_DECODER_WAIT + / 1000); add_timer(&hdw->quiescent_timer); } } @@ -3723,14 +3958,19 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) static int state_eval_usbstream_run(struct pvr2_hdw *hdw) { if (hdw->state_usbstream_run) { + int fl = !0; if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { - if (hdw->state_encoder_ok && - hdw->state_encoder_run && - hdw->state_pathway_ok) return 0; - } else { - if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause && - hdw->state_pathway_ok) return 0; + fl = (hdw->state_encoder_ok && + hdw->state_encoder_run); + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + fl = hdw->state_encoder_ok; + } + if (fl && + hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) { + return 0; } pvr2_hdw_cmd_usbstream(hdw,0); hdw->state_usbstream_run = 0; @@ -3741,6 +3981,19 @@ static int state_eval_usbstream_run(struct pvr2_hdw *hdw) if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { if (!hdw->state_encoder_ok || !hdw->state_encoder_run) return 0; + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + if (!hdw->state_encoder_ok) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) { + /* OnAir digital receivers won't stream + unless the analog encoder has run first. + Why? I have no idea. But don't even + try until we know the analog side is + known to have run. */ + if (!hdw->state_encoder_runok) return 0; + } } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; @@ -3836,6 +4089,24 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) } +static unsigned int print_input_mask(unsigned int msk, + char *buf,unsigned int acnt) +{ + unsigned int idx,ccnt; + unsigned int tcnt = 0; + for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) { + if (!((1 << idx) & msk)) continue; + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "%s%s", + (tcnt ? ", " : ""), + control_values_input[idx]); + tcnt += ccnt; + } + return tcnt; +} + + static const char *pvr2_pathway_state_name(int id) { switch (id) { @@ -3884,22 +4155,65 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_ok ? "" : " <encode:init>"), (hdw->state_encoder_run ? - " <encode:run>" : " <encode:stop>"), + (hdw->state_encoder_runok ? + " <encode:run>" : + " <encode:firstrun>") : + (hdw->state_encoder_runok ? + " <encode:stop>" : + " <encode:virgin>")), (hdw->state_encoder_config ? " <encode:configok>" : (hdw->state_encoder_waitok ? - "" : " <encode:wait>")), + "" : " <encode:waitok>")), (hdw->state_usbstream_run ? " <usb:run>" : " <usb:stop>"), (hdw->state_pathway_ok ? " <pathway:ok>" : "")); - break; case 3: return scnprintf( buf,acnt, "state: %s", pvr2_get_state_name(hdw->master_state)); - break; + case 4: { + unsigned int tcnt = 0; + unsigned int ccnt; + + ccnt = scnprintf(buf, + acnt, + "Hardware supported inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_avail_mask, + buf+tcnt, + acnt-tcnt); + if (hdw->input_avail_mask != hdw->input_allowed_mask) { + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "; allowed inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_allowed_mask, + buf+tcnt, + acnt-tcnt); + } + return tcnt; + } + case 5: { + struct pvr2_stream_stats stats; + if (!hdw->vid_stream) break; + pvr2_stream_get_stats(hdw->vid_stream, + &stats, + 0); + return scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + } default: break; } return 0; @@ -3963,7 +4277,9 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) st = PVR2_STATE_DEAD; } else if (hdw->fw1_state != FW1_STATE_OK) { st = PVR2_STATE_COLD; - } else if (analog_mode && !hdw->state_encoder_ok) { + } else if ((analog_mode || + hdw->hdw_desc->flag_digital_requires_cx23416) && + !hdw->state_encoder_ok) { st = PVR2_STATE_WARM; } else if (hdw->flag_tripped || (analog_mode && hdw->flag_decoder_missed)) { @@ -4009,6 +4325,7 @@ static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) queue_work(hdw->workqueue,&hdw->workpoll); } +#if 0 void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, struct pvr2_hdw_debug_info *ptr) @@ -4050,6 +4367,7 @@ void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, } while(0); LOCK_GIVE(hdw->ctl_lock); } +#endif /* 0 */ int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) { @@ -4116,6 +4434,74 @@ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) } +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw) +{ + return hdw->input_allowed_mask; +} + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v) +{ + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + return 0; +} + + +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw, + unsigned int change_mask, + unsigned int change_val) +{ + int ret = 0; + unsigned int nv,m,idx; + LOCK_TAKE(hdw->big_lock); + do { + nv = hdw->input_allowed_mask & ~change_mask; + nv |= (change_val & change_mask); + nv &= hdw->input_avail_mask; + if (!nv) { + /* No legal modes left; return error instead. */ + ret = -EPERM; + break; + } + hdw->input_allowed_mask = nv; + if ((1 << hdw->input_val) & hdw->input_allowed_mask) { + /* Current mode is still in the allowed mask, so + we're done. */ + break; + } + /* Select and switch to a mode that is still in the allowed + mask */ + if (!hdw->input_allowed_mask) { + /* Nothing legal; give up */ + break; + } + m = hdw->input_allowed_mask; + for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + pvr2_hdw_set_input(hdw,idx); + break; + } + } while (0); + LOCK_GIVE(hdw->big_lock); + return ret; +} + + /* Find I2C address of eeprom */ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index e3e965546..4e60f6262 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -92,9 +91,6 @@ enum pvr2_v4l_type { /* Translate configuration enum to a string label */ const char *pvr2_config_get_name(enum pvr2_config); -/* Translate a master state enum to a string label */ -const char *pvr2_hdw_get_state_name(unsigned int); - struct pvr2_hdw; /* Create and return a structure for interacting with the underlying @@ -158,6 +154,19 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); * will be according to PVR_CVAL_INPUT_xxxx definitions. */ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); +/* Return a bit mask of allowed input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *); + +/* Change the set of allowed input selections for this device. Both + change_mask and change_valu are mask bits according to + PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate + which settings are being changed and the change_val parameter indicates + whether corresponding settings are being set or cleared. */ +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *, + unsigned int change_mask, + unsigned int change_val); + /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 4b0ded374..25e611b2e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 7ce929be8..d715c80ef 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index c838df616..7fa38683b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index ddf357d1b..362ec49f5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h index bd0807b90..6ef7a1c0e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c index e4aae2c97..bf7bd5906 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -100,6 +99,10 @@ struct pvr2_stream { /* Tracking state for tolerating errors */ unsigned int fail_count; unsigned int fail_tolerance; + + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; }; struct pvr2_buffer { @@ -470,6 +473,8 @@ static void buffer_complete(struct urb *urb) (urb->status == -ENOENT) || (urb->status == -ECONNRESET) || (urb->status == -ESHUTDOWN)) { + (sp->buffers_processed)++; + sp->bytes_processed += urb->actual_length; bp->used_count = urb->actual_length; if (sp->fail_count) { pvr2_trace(PVR2_TRACE_TOLERANCE, @@ -481,11 +486,13 @@ static void buffer_complete(struct urb *urb) // We can tolerate this error, because we're below the // threshold... (sp->fail_count)++; + (sp->buffers_failed)++; pvr2_trace(PVR2_TRACE_TOLERANCE, "stream %p ignoring error %d" " - fail count increased to %u", sp,urb->status,sp->fail_count); } else { + (sp->buffers_failed)++; bp->status = urb->status; } spin_unlock_irqrestore(&sp->list_lock,irq_flags); @@ -539,6 +546,28 @@ void pvr2_stream_set_callback(struct pvr2_stream *sp, } while(0); mutex_unlock(&sp->mutex); } +void pvr2_stream_get_stats(struct pvr2_stream *sp, + struct pvr2_stream_stats *stats, + int zero_counts) +{ + unsigned long irq_flags; + spin_lock_irqsave(&sp->list_lock,irq_flags); + if (stats) { + stats->buffers_in_queue = sp->q_count; + stats->buffers_in_idle = sp->i_count; + stats->buffers_in_ready = sp->r_count; + stats->buffers_processed = sp->buffers_processed; + stats->buffers_failed = sp->buffers_failed; + stats->bytes_processed = sp->bytes_processed; + } + if (zero_counts) { + sp->buffers_processed = 0; + sp->buffers_failed = 0; + sp->bytes_processed = 0; + } + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + /* Query / set the nominal buffer count */ int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.h b/linux/drivers/media/video/pvrusb2/pvrusb2-io.h index b43e5f4cf..0a4b9a114 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -36,6 +35,15 @@ enum pvr2_buffer_state { struct pvr2_stream; struct pvr2_buffer; +struct pvr2_stream_stats { + unsigned int buffers_in_queue; + unsigned int buffers_in_idle; + unsigned int buffers_in_ready; + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; +}; + /* Initialize / tear down stream structure */ struct pvr2_stream *pvr2_stream_create(void); void pvr2_stream_destroy(struct pvr2_stream *); @@ -45,6 +53,9 @@ void pvr2_stream_setup(struct pvr2_stream *, void pvr2_stream_set_callback(struct pvr2_stream *, pvr2_stream_callback func, void *data); +void pvr2_stream_get_stats(struct pvr2_stream *, + struct pvr2_stream_stats *, + int zero_counts); /* Query / set the nominal buffer count */ int pvr2_stream_get_buffer_count(struct pvr2_stream *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c index d7bbbe00f..fb3ad639f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.h index fe397e987..f98dafac7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index 59f8d7298..ff85b381c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -61,6 +60,10 @@ static void pvr_setup_attach(struct pvr2_context *pvr) { /* Create association with v4l layer */ pvr2_v4l2_create(pvr); +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* Create association with dvb layer */ + pvr2_dvb_create(pvr); +#endif #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_create(pvr,class_ptr); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ @@ -125,6 +128,12 @@ static int __init pvr_init(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); + ret = pvr2_context_global_init(); + if (ret != 0) { + pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret); + return ret; + } + #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS class_ptr = pvr2_sysfs_class_create(); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ @@ -136,6 +145,8 @@ static int __init pvr_init(void) if (pvrusb2_debug) info("Debug mask is %d (0x%x)", pvrusb2_debug,pvrusb2_debug); + pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); + return ret; } @@ -148,6 +159,10 @@ static void __exit pvr_exit(void) #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_class_destroy(class_ptr); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + + pvr2_context_global_done(); + + pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete"); } module_init(pvr_init); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c index bd7a8de95..1e91401ba 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.h b/linux/drivers/media/video/pvrusb2/pvrusb2-std.h index 07c399375..a35c53d0b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index f672473dd..f6ace8abe 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -21,7 +20,6 @@ #include <linux/string.h> #include <linux/slab.h> -#include <asm/semaphore.h> #include "pvrusb2-sysfs.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" @@ -73,6 +71,7 @@ struct pvr2_sysfs_ctl_item { struct device_attribute attr_val; struct device_attribute attr_custom; struct pvr2_ctrl *cptr; + int ctl_id; struct pvr2_sysfs *chptr; struct pvr2_sysfs_ctl_item *item_next; struct attribute *attr_gen[7]; @@ -85,38 +84,29 @@ struct pvr2_sysfs_class { struct class class; }; -static ssize_t show_name(int id,struct device *class_dev,char *buf) +static ssize_t show_name(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; const char *name; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - name = pvr2_ctrl_get_desc(cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name); - + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name); + name = pvr2_ctrl_get_desc(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s", + cip->chptr, cip->ctl_id, name); if (!name) return -EINVAL; - - return scnprintf(buf,PAGE_SIZE,"%s\n",name); + return scnprintf(buf, PAGE_SIZE, "%s\n", name); } -static ssize_t show_type(int id,struct device *class_dev,char *buf) +static ssize_t show_type(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; const char *name; enum pvr2_ctl_type tp; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - tp = pvr2_ctrl_get_type(cptr); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type); + tp = pvr2_ctrl_get_type(cip->cptr); switch (tp) { case pvr2_ctl_int: name = "integer"; break; case pvr2_ctl_enum: name = "enum"; break; @@ -124,403 +114,178 @@ static ssize_t show_type(int id,struct device *class_dev,char *buf) case pvr2_ctl_bool: name = "boolean"; break; default: name = "?"; break; } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name); - + pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s", + cip->chptr, cip->ctl_id, name); if (!name) return -EINVAL; - - return scnprintf(buf,PAGE_SIZE,"%s\n",name); + return scnprintf(buf, PAGE_SIZE, "%s\n", name); } -static ssize_t show_min(int id,struct device *class_dev,char *buf) +static ssize_t show_min(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; long val; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - val = pvr2_ctrl_get_min(cptr); - - pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val); - - return scnprintf(buf,PAGE_SIZE,"%ld\n",val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min); + val = pvr2_ctrl_get_min(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld", + cip->chptr, cip->ctl_id, val); + return scnprintf(buf, PAGE_SIZE, "%ld\n", val); } -static ssize_t show_max(int id,struct device *class_dev,char *buf) +static ssize_t show_max(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; long val; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - val = pvr2_ctrl_get_max(cptr); - - pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val); - - return scnprintf(buf,PAGE_SIZE,"%ld\n",val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max); + val = pvr2_ctrl_get_max(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld", + cip->chptr, cip->ctl_id, val); + return scnprintf(buf, PAGE_SIZE, "%ld\n", val); } -static ssize_t show_val_norm(int id,struct device *class_dev,char *buf) +static ssize_t show_val_norm(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; - int val,ret; + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; unsigned int cnt = 0; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - ret = pvr2_ctrl_get_value(cptr,&val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); + ret = pvr2_ctrl_get_value(cip->cptr, &val); if (ret < 0) return ret; - - ret = pvr2_ctrl_value_to_sym(cptr,~0,val, - buf,PAGE_SIZE-1,&cnt); - + ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", - sfp,id,cnt,buf,val); + cip->chptr, cip->ctl_id, cnt, buf, val); buf[cnt] = '\n'; return cnt+1; } -static ssize_t show_val_custom(int id,struct device *class_dev,char *buf) +static ssize_t show_val_custom(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; - int val,ret; + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; unsigned int cnt = 0; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - ret = pvr2_ctrl_get_value(cptr,&val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); + ret = pvr2_ctrl_get_value(cip->cptr, &val); if (ret < 0) return ret; - - ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val, - buf,PAGE_SIZE-1,&cnt); - + ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", - sfp,id,cnt,buf,val); + cip->chptr, cip->ctl_id, cnt, buf, val); buf[cnt] = '\n'; return cnt+1; } -static ssize_t show_enum(int id,struct device *class_dev,char *buf) +static ssize_t show_enum(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; long val; - unsigned int bcnt,ccnt,ecnt; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - ecnt = pvr2_ctrl_get_cnt(cptr); + unsigned int bcnt, ccnt, ecnt; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum); + ecnt = pvr2_ctrl_get_cnt(cip->cptr); bcnt = 0; for (val = 0; val < ecnt; val++) { - pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt, + PAGE_SIZE - bcnt, &ccnt); if (!ccnt) continue; bcnt += ccnt; if (bcnt >= PAGE_SIZE) break; buf[bcnt] = '\n'; bcnt++; } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)", + cip->chptr, cip->ctl_id); return bcnt; } -static ssize_t show_bits(int id,struct device *class_dev,char *buf) +static ssize_t show_bits(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; - int valid_bits,msk; - unsigned int bcnt,ccnt; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - valid_bits = pvr2_ctrl_get_mask(cptr); + struct pvr2_sysfs_ctl_item *cip; + int valid_bits, msk; + unsigned int bcnt, ccnt; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits); + valid_bits = pvr2_ctrl_get_mask(cip->cptr); bcnt = 0; for (msk = 1; valid_bits; msk <<= 1) { if (!(msk & valid_bits)) continue; valid_bits &= ~msk; - pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt, + PAGE_SIZE - bcnt, &ccnt); bcnt += ccnt; if (bcnt >= PAGE_SIZE) break; buf[bcnt] = '\n'; bcnt++; } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)", + cip->chptr, cip->ctl_id); return bcnt; } -static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp, +static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl, const char *buf,unsigned int count) { - struct pvr2_ctrl *cptr; int ret; int mask,val; - - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); if (customfl) { - ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val); + ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count, + &mask, &val); } else { - ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val); + ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count, + &mask, &val); } if (ret < 0) return ret; - ret = pvr2_ctrl_set_mask_value(cptr,mask,val); - pvr2_hdw_commit_ctl(sfp->channel.hdw); + ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val); + pvr2_hdw_commit_ctl(cip->chptr->channel.hdw); return ret; } -static ssize_t store_val_norm(int id,struct device *class_dev, - const char *buf,size_t count) +static ssize_t store_val_norm(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; int ret; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", - sfp,id,(int)count,buf); - ret = store_val_any(id,0,sfp,buf,count); + cip->chptr, cip->ctl_id, (int)count, buf); + ret = store_val_any(cip, 0, buf, count); if (!ret) ret = count; return ret; } -static ssize_t store_val_custom(int id,struct device *class_dev, - const char *buf,size_t count) +static ssize_t store_val_custom(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; int ret; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", - sfp,id,(int)count,buf); - ret = store_val_any(id,1,sfp,buf,count); + cip->chptr, cip->ctl_id, (int)count, buf); + ret = store_val_any(cip, 1, buf, count); if (!ret) ret = count; return ret; } -/* - Mike Isely <isely@pobox.com> 30-April-2005 - - This next batch of horrible preprocessor hackery is needed because the - kernel's device_attribute mechanism fails to pass the actual - attribute through to the show / store functions, which means we have no - way to package up any attribute-specific parameters, like for example the - control id. So we work around this brain-damage by encoding the control - id into the show / store functions themselves and pick the function based - on the control id we're setting up. These macros try to ease the pain. - Yuck. -*/ - -#define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \ -static ssize_t sf_name##_##ctl_id(struct device *class_dev, \ -struct device_attribute *attr, char *buf) \ -{ return sf_name(ctl_id,class_dev,buf); } - -#define CREATE_STORE_INSTANCE(sf_name,ctl_id) \ -static ssize_t sf_name##_##ctl_id(struct device *class_dev, \ -struct device_attribute *attr, const char *buf, size_t count) \ -{ return sf_name(ctl_id,class_dev,buf,count); } - -#define CREATE_BATCH(ctl_id) \ -CREATE_SHOW_INSTANCE(show_name,ctl_id) \ -CREATE_SHOW_INSTANCE(show_type,ctl_id) \ -CREATE_SHOW_INSTANCE(show_min,ctl_id) \ -CREATE_SHOW_INSTANCE(show_max,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \ -CREATE_SHOW_INSTANCE(show_enum,ctl_id) \ -CREATE_SHOW_INSTANCE(show_bits,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \ - -CREATE_BATCH(0) -CREATE_BATCH(1) -CREATE_BATCH(2) -CREATE_BATCH(3) -CREATE_BATCH(4) -CREATE_BATCH(5) -CREATE_BATCH(6) -CREATE_BATCH(7) -CREATE_BATCH(8) -CREATE_BATCH(9) -CREATE_BATCH(10) -CREATE_BATCH(11) -CREATE_BATCH(12) -CREATE_BATCH(13) -CREATE_BATCH(14) -CREATE_BATCH(15) -CREATE_BATCH(16) -CREATE_BATCH(17) -CREATE_BATCH(18) -CREATE_BATCH(19) -CREATE_BATCH(20) -CREATE_BATCH(21) -CREATE_BATCH(22) -CREATE_BATCH(23) -CREATE_BATCH(24) -CREATE_BATCH(25) -CREATE_BATCH(26) -CREATE_BATCH(27) -CREATE_BATCH(28) -CREATE_BATCH(29) -CREATE_BATCH(30) -CREATE_BATCH(31) -CREATE_BATCH(32) -CREATE_BATCH(33) -CREATE_BATCH(34) -CREATE_BATCH(35) -CREATE_BATCH(36) -CREATE_BATCH(37) -CREATE_BATCH(38) -CREATE_BATCH(39) -CREATE_BATCH(40) -CREATE_BATCH(41) -CREATE_BATCH(42) -CREATE_BATCH(43) -CREATE_BATCH(44) -CREATE_BATCH(45) -CREATE_BATCH(46) -CREATE_BATCH(47) -CREATE_BATCH(48) -CREATE_BATCH(49) -CREATE_BATCH(50) -CREATE_BATCH(51) -CREATE_BATCH(52) -CREATE_BATCH(53) -CREATE_BATCH(54) -CREATE_BATCH(55) -CREATE_BATCH(56) -CREATE_BATCH(57) -CREATE_BATCH(58) -CREATE_BATCH(59) - -struct pvr2_sysfs_func_set { - ssize_t (*show_name)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_type)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_min)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_max)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_enum)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_bits)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_val_norm)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*store_val_norm)(struct device *, - struct device_attribute *attr, - const char *,size_t); - ssize_t (*show_val_custom)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*store_val_custom)(struct device *, - struct device_attribute *attr, - const char *,size_t); -}; - -#define INIT_BATCH(ctl_id) \ -[ctl_id] = { \ - .show_name = show_name_##ctl_id, \ - .show_type = show_type_##ctl_id, \ - .show_min = show_min_##ctl_id, \ - .show_max = show_max_##ctl_id, \ - .show_enum = show_enum_##ctl_id, \ - .show_bits = show_bits_##ctl_id, \ - .show_val_norm = show_val_norm_##ctl_id, \ - .store_val_norm = store_val_norm_##ctl_id, \ - .show_val_custom = show_val_custom_##ctl_id, \ - .store_val_custom = store_val_custom_##ctl_id, \ -} \ - -static struct pvr2_sysfs_func_set funcs[] = { - INIT_BATCH(0), - INIT_BATCH(1), - INIT_BATCH(2), - INIT_BATCH(3), - INIT_BATCH(4), - INIT_BATCH(5), - INIT_BATCH(6), - INIT_BATCH(7), - INIT_BATCH(8), - INIT_BATCH(9), - INIT_BATCH(10), - INIT_BATCH(11), - INIT_BATCH(12), - INIT_BATCH(13), - INIT_BATCH(14), - INIT_BATCH(15), - INIT_BATCH(16), - INIT_BATCH(17), - INIT_BATCH(18), - INIT_BATCH(19), - INIT_BATCH(20), - INIT_BATCH(21), - INIT_BATCH(22), - INIT_BATCH(23), - INIT_BATCH(24), - INIT_BATCH(25), - INIT_BATCH(26), - INIT_BATCH(27), - INIT_BATCH(28), - INIT_BATCH(29), - INIT_BATCH(30), - INIT_BATCH(31), - INIT_BATCH(32), - INIT_BATCH(33), - INIT_BATCH(34), - INIT_BATCH(35), - INIT_BATCH(36), - INIT_BATCH(37), - INIT_BATCH(38), - INIT_BATCH(39), - INIT_BATCH(40), - INIT_BATCH(41), - INIT_BATCH(42), - INIT_BATCH(43), - INIT_BATCH(44), - INIT_BATCH(45), - INIT_BATCH(46), - INIT_BATCH(47), - INIT_BATCH(48), - INIT_BATCH(49), - INIT_BATCH(50), - INIT_BATCH(51), - INIT_BATCH(52), - INIT_BATCH(53), - INIT_BATCH(54), - INIT_BATCH(55), - INIT_BATCH(56), - INIT_BATCH(57), - INIT_BATCH(58), - INIT_BATCH(59), -}; - - static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) { struct pvr2_sysfs_ctl_item *cip; - struct pvr2_sysfs_func_set *fp; struct pvr2_ctrl *cptr; unsigned int cnt,acnt; int ret; - if ((ctl_id < 0) || (ctl_id >= ARRAY_SIZE(funcs))) { - return; - } - - fp = funcs + ctl_id; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); if (!cptr) return; @@ -529,6 +294,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); cip->cptr = cptr; + cip->ctl_id = ctl_id; cip->chptr = sfp; cip->item_next = NULL; @@ -541,19 +307,19 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_name.attr.name = "name"; cip->attr_name.attr.mode = S_IRUGO; - cip->attr_name.show = fp->show_name; + cip->attr_name.show = show_name; cip->attr_type.attr.name = "type"; cip->attr_type.attr.mode = S_IRUGO; - cip->attr_type.show = fp->show_type; + cip->attr_type.show = show_type; cip->attr_min.attr.name = "min_val"; cip->attr_min.attr.mode = S_IRUGO; - cip->attr_min.show = fp->show_min; + cip->attr_min.show = show_min; cip->attr_max.attr.name = "max_val"; cip->attr_max.attr.mode = S_IRUGO; - cip->attr_max.show = fp->show_max; + cip->attr_max.show = show_max; cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; @@ -563,11 +329,11 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_enum.attr.name = "enum_val"; cip->attr_enum.attr.mode = S_IRUGO; - cip->attr_enum.show = fp->show_enum; + cip->attr_enum.show = show_enum; cip->attr_bits.attr.name = "bit_val"; cip->attr_bits.attr.mode = S_IRUGO; - cip->attr_bits.show = fp->show_bits; + cip->attr_bits.show = show_bits; if (pvr2_ctrl_is_writable(cptr)) { cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; @@ -578,12 +344,12 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_gen[acnt++] = &cip->attr_name.attr; cip->attr_gen[acnt++] = &cip->attr_type.attr; cip->attr_gen[acnt++] = &cip->attr_val.attr; - cip->attr_val.show = fp->show_val_norm; - cip->attr_val.store = fp->store_val_norm; + cip->attr_val.show = show_val_norm; + cip->attr_val.store = store_val_norm; if (pvr2_ctrl_has_custom_symbols(cptr)) { cip->attr_gen[acnt++] = &cip->attr_custom.attr; - cip->attr_custom.show = fp->show_val_custom; - cip->attr_custom.store = fp->store_val_custom; + cip->attr_custom.show = show_val_custom; + cip->attr_custom.store = store_val_custom; } switch (pvr2_ctrl_get_type(cptr)) { case pvr2_ctl_enum: @@ -610,8 +376,9 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); if (ret) { - printk(KERN_WARNING "%s: sysfs_create_group error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "sysfs_create_group error: %d", + ret); return; } cip->created_ok = !0; @@ -642,15 +409,17 @@ static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) sfp->debugifc = dip; ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { dip->debugcmd_created_ok = !0; } ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { dip->debuginfo_created_ok = !0; } @@ -861,8 +630,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->driver_data = sfp; ret = device_register(class_dev); if (ret) { - printk(KERN_ERR "%s: device_register failed\n", - __FUNCTION__); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_register failed"); kfree(class_dev); return; } @@ -874,8 +643,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_v4l_minor_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->v4l_minor_number_created_ok = !0; } @@ -887,8 +657,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_v4l_radio_minor_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->v4l_radio_minor_number_created_ok = !0; } @@ -899,8 +670,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->attr_unit_number.store = NULL; ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->unit_number_created_ok = !0; } @@ -912,8 +684,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_bus_info); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->bus_info_created_ok = !0; } @@ -925,8 +698,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_hdw_name); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->hdw_name_created_ok = !0; } @@ -938,8 +712,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_hdw_desc); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->hdw_desc_created_ok = !0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.h index ff9373b47..6d875bfe7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c index c65dbe727..efec78516 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.h index 556f12aa9..ef4afaf37 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-util.h b/linux/drivers/media/video/pvrusb2/pvrusb2-util.h index e53aee416..92b75544e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-util.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-util.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 38b0ff0f5..55cb4021a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -58,7 +57,9 @@ struct pvr2_v4l2_fh { struct pvr2_v4l2_fh *vprev; wait_queue_head_t wait_data; int fw_mode_flag; - int prev_input_val; + /* Map contiguous ordinal value to input id */ + unsigned char *input_map; + unsigned int input_cnt; }; struct pvr2_v4l2 { @@ -68,10 +69,6 @@ struct pvr2_v4l2 { struct v4l2_prio_state prio; - /* Map contiguous ordinal value to input id */ - unsigned char *input_map; - unsigned int input_cnt; - /* streams - Note that these must be separately, individually, * allocated pointers. This is because the v4l core is going to * manage their deletion - separately, individually... */ @@ -271,11 +268,11 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; ret = 0; - if ((vi->index < 0) || (vi->index >= vp->input_cnt)) { + if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { ret = -EINVAL; break; } - val = vp->input_map[vi->index]; + val = fh->input_map[vi->index]; switch (val) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_DTV: @@ -323,8 +320,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, val = 0; ret = pvr2_ctrl_get_value(cptr,&val); vi->index = 0; - for (idx = 0; idx < vp->input_cnt; idx++) { - if (vp->input_map[idx] == val) { + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { vi->index = idx; break; } @@ -335,13 +332,13 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_S_INPUT: { struct v4l2_input *vi = (struct v4l2_input *)arg; - if ((vi->index < 0) || (vi->index >= vp->input_cnt)) { + if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { ret = -ERANGE; break; } ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - vp->input_map[vi->index]); + fh->input_map[vi->index]); break; } @@ -840,10 +837,6 @@ static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) pvr2_v4l2_dev_destroy(vp->dev_radio); vp->dev_radio = NULL; } - if (vp->input_map) { - kfree(vp->input_map); - vp->input_map = NULL; - } pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); pvr2_channel_done(&vp->channel); @@ -901,20 +894,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; - /* Restore the previous input selection, if it makes sense - to do so. */ - if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - int pval; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&pval); - /* Only restore if we're still selecting the radio */ - if (pval == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(cp,fhp->prev_input_val); - pvr2_hdw_commit_ctl(hdw); - } - } - if (fhp->vnext) { fhp->vnext->vprev = fhp->vprev; } else { @@ -931,6 +910,10 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) pvr2_channel_done(&fhp->channel); pvr2_trace(PVR2_TRACE_STRUCT, "Destroying pvr_v4l2_fh id=%p",fhp); + if (fhp->input_map) { + kfree(fhp->input_map); + fhp->input_map = NULL; + } kfree(fhp); if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { pvr2_v4l2_destroy_no_lock(vp); @@ -945,6 +928,9 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) struct pvr2_v4l2_fh *fhp; struct pvr2_v4l2 *vp; struct pvr2_hdw *hdw; + unsigned int input_mask = 0; + unsigned int input_cnt,idx; + int ret = 0; dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); @@ -970,6 +956,50 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); pvr2_channel_init(&fhp->channel,vp->channel.mc_head); + if (dip->v4l_type == VFL_TYPE_RADIO) { + /* Opening device as a radio, legal input selection subset + is just the radio. */ + input_mask = (1 << PVR2_CVAL_INPUT_RADIO); + } else { + /* Opening the main V4L device, legal input selection + subset includes all analog inputs. */ + input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) | + (1 << PVR2_CVAL_INPUT_TV) | + (1 << PVR2_CVAL_INPUT_COMPOSITE) | + (1 << PVR2_CVAL_INPUT_SVIDEO)); + } + ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask); + if (ret) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input mask error)", + fhp); + + kfree(fhp); + return ret; + } + + input_mask &= pvr2_hdw_get_input_available(hdw); + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (input_mask & (1 << idx)) input_cnt++; + } + fhp->input_cnt = input_cnt; + fhp->input_map = kzalloc(input_cnt,GFP_KERNEL); + if (!fhp->input_map) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input map failure)", + fhp); + kfree(fhp); + return -ENOMEM; + } + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (!(input_mask & (1 << idx))) continue; + fhp->input_map[input_cnt++] = idx; + } + fhp->vnext = NULL; fhp->vprev = vp->vlast; if (vp->vlast) { @@ -980,18 +1010,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) vp->vlast = fhp; fhp->vhead = vp; - /* Opening the /dev/radioX device implies a mode switch. - So execute that here. Note that you can get the - IDENTICAL effect merely by opening the normal video - device and setting the input appropriately. */ - if (dip->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&fhp->prev_input_val); - pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); - pvr2_hdw_commit_ctl(hdw); - } - fhp->file = file; file->private_data = fhp; v4l2_prio_open(&vp->prio,&fhp->prio); @@ -1226,8 +1244,6 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) { struct pvr2_v4l2 *vp; - struct pvr2_hdw *hdw; - unsigned int input_mask,input_cnt,idx; vp = kzalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; @@ -1236,26 +1252,12 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) vp->channel.check_func = pvr2_v4l2_internal_check; - hdw = vp->channel.mc_head->hdw; - input_mask = pvr2_hdw_get_input_available(hdw); - input_cnt = 0; - for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { - if (input_mask & (1 << idx)) input_cnt++; - } - vp->input_cnt = input_cnt; - vp->input_map = kzalloc(input_cnt,GFP_KERNEL); - if (!vp->input_map) goto fail; - input_cnt = 0; - for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { - if (!(input_mask & (1 << idx))) continue; - vp->input_map[input_cnt++] = idx; - } - /* register streams */ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); if (!vp->dev_video) goto fail; pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); - if (input_mask & (1 << PVR2_CVAL_INPUT_RADIO)) { + if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & + (1 << PVR2_CVAL_INPUT_RADIO)) { vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); if (!vp->dev_radio) goto fail; pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); @@ -1265,7 +1267,7 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) fail: pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp); pvr2_v4l2_destroy_no_lock(vp); - return 0; + return NULL; } /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.h index 9a995e2d2..34c011a7b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 36ce6dc66..b1ff3803f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -82,7 +81,7 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != 0) && + ((sp = routing_schemes + sid) != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { route.input = sp->def[hdw->input_val]; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h index 2b917fda0..4ff5b892b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index d578f2491..6cdcbf2fe 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h index 8aaeff4e1..807090961 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2.h b/linux/drivers/media/video/pvrusb2/pvrusb2.h index 1a9a4baf1..240de9b35 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 936db67a5..7cc8e9b19 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -49,6 +49,9 @@ #define CICR1_DW_VAL(x) ((x) & CICR1_DW) /* Data bus width */ #define CICR1_PPL_VAL(x) (((x) << 15) & CICR1_PPL) /* Pixels per line */ +#define CICR1_COLOR_SP_VAL(x) (((x) << 3) & CICR1_COLOR_SP) /* color space */ +#define CICR1_RGB_BPP_VAL(x) (((x) << 7) & CICR1_RGB_BPP) /* bpp for rgb */ +#define CICR1_RGBT_CONV_VAL(x) (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */ #define CICR2_BLW_VAL(x) (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */ #define CICR2_ELW_VAL(x) (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */ @@ -70,6 +73,19 @@ static DEFINE_MUTEX(camera_lock); /* * Structures */ +enum pxa_camera_active_dma { + DMA_Y = 0x1, + DMA_U = 0x2, + DMA_V = 0x4, +}; + +/* descriptor needed for the PXA DMA engine */ +struct pxa_cam_dma { + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + size_t sg_size; + int sglen; +}; /* buffer for one video frame */ struct pxa_buffer { @@ -78,16 +94,12 @@ struct pxa_buffer { const struct soc_camera_data_format *fmt; - /* our descriptor list needed for the PXA DMA engine */ - dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; - size_t sg_size; + /* our descriptor lists for Y, U and V channels */ + struct pxa_cam_dma dmas[3]; + int inwork; -}; -struct pxa_framebuffer_queue { - dma_addr_t sg_last_dma; - struct pxa_dma_desc *sg_last_cpu; + enum pxa_camera_active_dma active_dma; }; struct pxa_camera_dev { @@ -100,7 +112,9 @@ struct pxa_camera_dev { unsigned int irq; void __iomem *base; - unsigned int dma_chan_y; + + int channels; + unsigned int dma_chans[3]; struct pxacamera_platform_data *pdata; struct resource *res; @@ -112,6 +126,7 @@ struct pxa_camera_dev { spinlock_t lock; struct pxa_buffer *active; + struct pxa_dma_desc *sg_tail[3]; }; static const char *pxa_cam_driver_description = "PXA_Camera"; @@ -125,10 +140,21 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); - *size = icd->width * icd->height * ((icd->current_fmt->depth + 7) >> 3); + /* planar capture requires Y, U and V buffers to be page aligned */ + if (pcdev->channels == 3) { + *size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */ + *size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */ + *size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */ + } else { + *size = icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3); + } if (0 == *count) *count = 32; @@ -145,6 +171,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + int i; BUG_ON(in_interrupt()); @@ -157,14 +184,62 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) videobuf_dma_unmap(vq, dma); videobuf_dma_free(dma); - if (buf->sg_cpu) - dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu, - buf->sg_dma); - buf->sg_cpu = NULL; + for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { + if (buf->dmas[i].sg_cpu) + dma_free_coherent(pcdev->dev, buf->dmas[i].sg_size, + buf->dmas[i].sg_cpu, + buf->dmas[i].sg_dma); + buf->dmas[i].sg_cpu = NULL; + } buf->vb.state = VIDEOBUF_NEEDS_INIT; } +static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf, + struct videobuf_dmabuf *dma, int channel, + int sglen, int sg_start, int cibr, + unsigned int size) +{ + struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; + int i; + + if (pxa_dma->sg_cpu) + dma_free_coherent(pcdev->dev, pxa_dma->sg_size, + pxa_dma->sg_cpu, pxa_dma->sg_dma); + + pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); + pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, + &pxa_dma->sg_dma, GFP_KERNEL); + if (!pxa_dma->sg_cpu) + return -ENOMEM; + + pxa_dma->sglen = sglen; + + for (i = 0; i < sglen; i++) { + int sg_i = sg_start + i; + struct scatterlist *sg = dma->sglist; + unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; + + pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; + pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); + + /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ + xfer_len = (min(dma_len, size) + 7) & ~7; + + pxa_dma->sg_cpu[i].dcmd = + DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; + size -= dma_len; + pxa_dma->sg_cpu[i].ddadr = + pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); + } + + pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; + pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; + + return 0; +} + static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { @@ -173,7 +248,9 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - int i, ret; + int ret; + int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; + int size_y, size_u = 0, size_v = 0; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -218,49 +295,64 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, if (ret) goto fail; - if (buf->sg_cpu) - dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu, - buf->sg_dma); + if (pcdev->channels == 3) { + /* FIXME the calculations should be more precise */ + sglen_y = dma->sglen / 2; + sglen_u = sglen_v = dma->sglen / 4 + 1; + sglen_yu = sglen_y + sglen_u; + size_y = size / 2; + size_u = size_v = size / 4; + } else { + sglen_y = dma->sglen; + size_y = size; + } + + /* init DMA for Y channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, + 0, 0x28, size_y); - buf->sg_size = (dma->sglen + 1) * sizeof(struct pxa_dma_desc); - buf->sg_cpu = dma_alloc_coherent(pcdev->dev, buf->sg_size, - &buf->sg_dma, GFP_KERNEL); - if (!buf->sg_cpu) { - ret = -ENOMEM; + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for Y/RGB failed\n"); goto fail; } - dev_dbg(&icd->dev, "nents=%d size: %d sg=0x%p\n", - dma->sglen, size, dma->sglist); - for (i = 0; i < dma->sglen; i++) { - struct scatterlist *sg = dma->sglist; - unsigned int dma_len = sg_dma_len(&sg[i]), xfer_len; - - /* CIBR0 */ - buf->sg_cpu[i].dsadr = pcdev->res->start + 0x28; - buf->sg_cpu[i].dtadr = sg_dma_address(&sg[i]); - /* PXA270 Developer's Manual 27.4.4.1: - * round up to 8 bytes */ - xfer_len = (min(dma_len, size) + 7) & ~7; - if (xfer_len & 7) - dev_err(&icd->dev, "Unaligned buffer: " - "dma_len %u, size %u\n", dma_len, size); - buf->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | - DCMD_INCTRGADDR | xfer_len; - size -= dma_len; - buf->sg_cpu[i].ddadr = buf->sg_dma + (i + 1) * - sizeof(struct pxa_dma_desc); + if (pcdev->channels == 3) { + /* init DMA for U channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u, + sglen_y, 0x30, size_u); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for U failed\n"); + goto fail_u; + } + + /* init DMA for V channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, + sglen_yu, 0x38, size_v); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for V failed\n"); + goto fail_v; + } } - buf->sg_cpu[dma->sglen - 1].ddadr = DDADR_STOP; - buf->sg_cpu[dma->sglen - 1].dcmd |= DCMD_ENDIRQEN; vb->state = VIDEOBUF_PREPARED; } buf->inwork = 0; + buf->active_dma = DMA_Y; + if (pcdev->channels == 3) + buf->active_dma |= DMA_U | DMA_V; return 0; +fail_v: + dma_free_coherent(pcdev->dev, buf->dmas[1].sg_size, + buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); +fail_u: + dma_free_coherent(pcdev->dev, buf->dmas[0].sg_size, + buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); fail: free_buffer(vq, buf); out: @@ -277,9 +369,8 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); struct pxa_buffer *active; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - int nents = dma->sglen; unsigned long flags; + int i; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -292,59 +383,63 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, if (!active) { CIFR |= CIFR_RESET_F; - DDADR(pcdev->dma_chan_y) = buf->sg_dma; - DCSR(pcdev->dma_chan_y) = DCSR_RUN; + + for (i = 0; i < pcdev->channels; i++) { + DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + pcdev->sg_tail[i] = buf->dmas[i].sg_cpu + buf->dmas[i].sglen - 1; + } + pcdev->active = buf; CICR0 |= CICR0_ENB; } else { - struct videobuf_dmabuf *active_dma = - videobuf_to_dma(&active->vb); - /* Stop DMA engine */ - DCSR(pcdev->dma_chan_y) = 0; - - /* Add the descriptors we just initialized to the currently - * running chain - */ - active->sg_cpu[active_dma->sglen - 1].ddadr = buf->sg_dma; - - /* Setup a dummy descriptor with the DMA engines current - * state - */ - /* CIBR0 */ - buf->sg_cpu[nents].dsadr = pcdev->res->start + 0x28; - buf->sg_cpu[nents].dtadr = DTADR(pcdev->dma_chan_y); - buf->sg_cpu[nents].dcmd = DCMD(pcdev->dma_chan_y); - - if (DDADR(pcdev->dma_chan_y) == DDADR_STOP) { - /* The DMA engine is on the last descriptor, set the - * next descriptors address to the descriptors - * we just initialized - */ - buf->sg_cpu[nents].ddadr = buf->sg_dma; - } else { - buf->sg_cpu[nents].ddadr = DDADR(pcdev->dma_chan_y); - } + struct pxa_cam_dma *buf_dma; + struct pxa_cam_dma *act_dma; + int nents; - /* The next descriptor is the dummy descriptor */ - DDADR(pcdev->dma_chan_y) = buf->sg_dma + nents * - sizeof(struct pxa_dma_desc); + for (i = 0; i < pcdev->channels; i++) { + buf_dma = &buf->dmas[i]; + act_dma = &active->dmas[i]; + nents = buf_dma->sglen; -#ifdef DEBUG - if (CISR & CISR_IFO_0) { - dev_warn(pcdev->dev, "FIFO overrun\n"); - DDADR(pcdev->dma_chan_y) = pcdev->active->sg_dma; - - CICR0 &= ~CICR0_ENB; - CIFR |= CIFR_RESET_F; - DCSR(pcdev->dma_chan_y) = DCSR_RUN; - CICR0 |= CICR0_ENB; - } else -#endif - DCSR(pcdev->dma_chan_y) = DCSR_RUN; + /* Stop DMA engine */ + DCSR(pcdev->dma_chans[i]) = 0; + + /* Add the descriptors we just initialized to + the currently running chain */ + pcdev->sg_tail[i]->ddadr = buf_dma->sg_dma; + pcdev->sg_tail[i] = buf_dma->sg_cpu + buf_dma->sglen - 1; + + /* Setup a dummy descriptor with the DMA engines current + * state + */ + buf_dma->sg_cpu[nents].dsadr = + pcdev->res->start + 0x28 + i*8; /* CIBRx */ + buf_dma->sg_cpu[nents].dtadr = + DTADR(pcdev->dma_chans[i]); + buf_dma->sg_cpu[nents].dcmd = + DCMD(pcdev->dma_chans[i]); + + if (DDADR(pcdev->dma_chans[i]) == DDADR_STOP) { + /* The DMA engine is on the last + descriptor, set the next descriptors + address to the descriptors we just + initialized */ + buf_dma->sg_cpu[nents].ddadr = buf_dma->sg_dma; + } else { + buf_dma->sg_cpu[nents].ddadr = + DDADR(pcdev->dma_chans[i]); + } + + /* The next descriptor is the dummy descriptor */ + DDADR(pcdev->dma_chans[i]) = buf_dma->sg_dma + nents * + sizeof(struct pxa_dma_desc); + + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } } spin_unlock_irqrestore(&pcdev->lock, flags); - } static void pxa_videobuf_release(struct videobuf_queue *vq, @@ -376,18 +471,42 @@ static void pxa_videobuf_release(struct videobuf_queue *vq, free_buffer(vq, buf); } -static void pxa_camera_dma_irq_y(int channel, void *data) +static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, + struct videobuf_buffer *vb, + struct pxa_buffer *buf) +{ + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + DCSR(pcdev->dma_chans[0]) = 0; + DCSR(pcdev->dma_chans[1]) = 0; + DCSR(pcdev->dma_chans[2]) = 0; + CICR0 &= ~CICR0_ENB; + return; + } + + pcdev->active = list_entry(pcdev->capture.next, + struct pxa_buffer, vb.queue); +} + +static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, + enum pxa_camera_active_dma act_dma) { - struct pxa_camera_dev *pcdev = data; struct pxa_buffer *buf; unsigned long flags; - unsigned int status; + u32 status, camera_status, overrun; struct videobuf_buffer *vb; spin_lock_irqsave(&pcdev->lock, flags); - status = DCSR(pcdev->dma_chan_y); - DCSR(pcdev->dma_chan_y) = status; + status = DCSR(channel); + DCSR(channel) = status | DCSR_ENDINTR; if (status & DCSR_BUSERR) { dev_err(pcdev->dev, "DMA Bus Error IRQ!\n"); @@ -405,33 +524,57 @@ static void pxa_camera_dma_irq_y(int channel, void *data) goto out; } + camera_status = CISR; + overrun = CISR_IFO_0; + if (pcdev->channels == 3) + overrun |= CISR_IFO_1 | CISR_IFO_2; + if (camera_status & overrun) { + dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status); + /* Stop the Capture Interface */ + CICR0 &= ~CICR0_ENB; + /* Stop DMA */ + DCSR(channel) = 0; + /* Reset the FIFOs */ + CIFR |= CIFR_RESET_F; + /* Enable End-Of-Frame Interrupt */ + CICR0 &= ~CICR0_EOFM; + /* Restart the Capture Interface */ + CICR0 |= CICR0_ENB; + goto out; + } + vb = &pcdev->active->vb; buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); - /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); - vb->field_count++; - wake_up(&vb->done); - - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - DCSR(pcdev->dma_chan_y) = 0; - CICR0 &= ~CICR0_ENB; - goto out; - } - - pcdev->active = list_entry(pcdev->capture.next, struct pxa_buffer, - vb.queue); + buf->active_dma &= ~act_dma; + if (!buf->active_dma) + pxa_camera_wakeup(pcdev, vb, buf); out: spin_unlock_irqrestore(&pcdev->lock, flags); } +static void pxa_camera_dma_irq_y(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_Y); +} + +static void pxa_camera_dma_irq_u(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_U); +} + +static void pxa_camera_dma_irq_v(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_V); +} + static struct videobuf_queue_ops pxa_videobuf_ops = { .buf_setup = pxa_videobuf_setup, .buf_prepare = pxa_videobuf_prepare, @@ -524,8 +667,21 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status); + if (!status) + return IRQ_NONE; + CISR = status; + if (status & CISR_EOF) { + int i; + for (i = 0; i < pcdev->channels; i++) { + DDADR(pcdev->dma_chans[i]) = + pcdev->active->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } + CICR0 |= CICR0_EOFM; + } + return IRQ_HANDLED; } @@ -571,8 +727,11 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) /* disable capture, disable interrupts */ CICR0 = 0x3ff; + /* Stop DMA engine */ - DCSR(pcdev->dma_chan_y) = 0; + DCSR(pcdev->dma_chans[0]) = 0; + DCSR(pcdev->dma_chans[1]) = 0; + DCSR(pcdev->dma_chans[2]) = 0; icd->ops->release(icd); @@ -625,7 +784,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long dw, bpp, bus_flags, camera_flags, common_flags; - u32 cicr0, cicr4 = 0; + u32 cicr0, cicr1, cicr4 = 0; int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); if (ret < 0) @@ -637,6 +796,8 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) if (!common_flags) return -EINVAL; + pcdev->channels = 1; + /* Make choises, based on platform preferences */ if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { @@ -702,7 +863,26 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) cicr0 = CICR0; if (cicr0 & CICR0_ENB) CICR0 = cicr0 & ~CICR0_ENB; - CICR1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + + cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + + switch (pixfmt) { + case V4L2_PIX_FMT_YUV422P: + pcdev->channels = 3; + cicr1 |= CICR1_YCBCR_F; + case V4L2_PIX_FMT_YUYV: + cicr1 |= CICR1_COLOR_SP_VAL(2); + break; + case V4L2_PIX_FMT_RGB555: + cicr1 |= CICR1_RGB_BPP_VAL(1) | CICR1_RGBT_CONV_VAL(2) | + CICR1_TBIT | CICR1_COLOR_SP_VAL(1); + break; + case V4L2_PIX_FMT_RGB565: + cicr1 |= CICR1_COLOR_SP_VAL(1) | CICR1_RGB_BPP_VAL(2); + break; + } + + CICR1 = cicr1; CICR2 = 0; CICR3 = CICR3_LPF_VAL(icd->height - 1) | CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); @@ -905,16 +1085,36 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; /* request dma */ - pcdev->dma_chan_y = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, - pxa_camera_dma_irq_y, pcdev); - if (pcdev->dma_chan_y < 0) { + pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, + pxa_camera_dma_irq_y, pcdev); + if (pcdev->dma_chans[0] < 0) { dev_err(pcdev->dev, "Can't request DMA for Y\n"); err = -ENOMEM; goto exit_iounmap; } - dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chan_y); + dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); + + pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH, + pxa_camera_dma_irq_u, pcdev); + if (pcdev->dma_chans[1] < 0) { + dev_err(pcdev->dev, "Can't request DMA for U\n"); + err = -ENOMEM; + goto exit_free_dma_y; + } + dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); + + pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH, + pxa_camera_dma_irq_v, pcdev); + if (pcdev->dma_chans[0] < 0) { + dev_err(pcdev->dev, "Can't request DMA for V\n"); + err = -ENOMEM; + goto exit_free_dma_u; + } + dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); - DRCMR68 = pcdev->dma_chan_y | DRCMR_MAPVLD; + DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; + DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD; + DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD; /* request irq */ err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, @@ -936,7 +1136,11 @@ static int pxa_camera_probe(struct platform_device *pdev) exit_free_irq: free_irq(pcdev->irq, pcdev); exit_free_dma: - pxa_free_dma(pcdev->dma_chan_y); + pxa_free_dma(pcdev->dma_chans[2]); +exit_free_dma_u: + pxa_free_dma(pcdev->dma_chans[1]); +exit_free_dma_y: + pxa_free_dma(pcdev->dma_chans[0]); exit_iounmap: iounmap(base); exit_release: @@ -956,7 +1160,9 @@ static int __devexit pxa_camera_remove(struct platform_device *pdev) clk_put(pcdev->clk); - pxa_free_dma(pcdev->dma_chan_y); + pxa_free_dma(pcdev->dma_chans[0]); + pxa_free_dma(pcdev->dma_chans[1]); + pxa_free_dma(pcdev->dma_chans[2]); free_irq(pcdev->irq, pcdev); soc_camera_host_unregister(&pxa_soc_camera_host); diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index a5ae60c7b..b02c43278 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -1461,7 +1461,8 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar /* ----------------------------------------------------------------------- */ -static int saa7115_probe(struct i2c_client *client) +static int saa7115_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct saa711x_state *state; int i; diff --git a/linux/drivers/media/video/saa7127.c b/linux/drivers/media/video/saa7127.c index ff2e70833..3acc869b7 100644 --- a/linux/drivers/media/video/saa7127.c +++ b/linux/drivers/media/video/saa7127.c @@ -679,7 +679,8 @@ static int saa7127_command(struct i2c_client *client, /* ----------------------------------------------------------------------- */ -static int saa7127_probe(struct i2c_client *client) +static int saa7127_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct saa7127_state *state; struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig index e086f14d5..83f076abc 100644 --- a/linux/drivers/media/video/saa7134/Kconfig +++ b/linux/drivers/media/video/saa7134/Kconfig @@ -27,6 +27,7 @@ config VIDEO_SAA7134_ALSA config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE + depends on HOTPLUG # due to FW_LOADER select VIDEOBUF_DVB select FW_LOADER select DVB_PLL if !DVB_FE_CUSTOMISE @@ -35,9 +36,9 @@ config VIDEO_SAA7134_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE - select DVB_TDA827X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/linux/drivers/media/video/saa7134/Makefile b/linux/drivers/media/video/saa7134/Makefile index 9aff937ba..3dbaa19a6 100644 --- a/linux/drivers/media/video/saa7134/Makefile +++ b/linux/drivers/media/video/saa7134/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-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 diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index 121b50e88..58670f9ea 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -969,10 +969,8 @@ static void snd_saa7134_free(struct snd_card * card) if (chip->dev->dmasound.priv_data == NULL) return; - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, &chip->dev->dmasound); - } chip->dev->dmasound.priv_data = NULL; diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 3bee2d71c..49bf24e7b 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -30,6 +30,7 @@ #include "tuner-xc2028.h" #include <media/v4l2-common.h> #include <media/tveeprom.h> +#include "tea5767.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -46,6 +47,9 @@ static char name_svideo[] = "S-Video"; /* ------------------------------------------------------------------ */ /* board config info */ +/* If radio_type !=UNSET, radio_addr should be specified + */ + struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_UNKNOWN] = { .name = "UNKNOWN/GENERIC", @@ -1049,7 +1053,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV002] = { /* Ognjen Nastic <ognjen@logosoft.ba> */ - .name = "Manli MuchTV M-TV002/Behold TV 403 FM", + .name = "Manli MuchTV M-TV002", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .radio_type = UNSET, @@ -1076,7 +1080,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV001] = { /* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */ - .name = "Manli MuchTV M-TV001/Behold TV 401", + .name = "Manli MuchTV M-TV001", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .radio_type = UNSET, @@ -2198,6 +2202,8 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_BEHOLD_409FM] = { /* <http://tuner.beholder.ru>, Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 409 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -2205,6 +2211,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3122,7 +3129,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_type = TUNER_PHILIPS_TD1316, /* untested */ .radio_type = TUNER_TEA5767, /* untested */ .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, + .radio_addr = 0x60, .tda9887_conf = TDA9887_PRESENT, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ @@ -3615,12 +3622,15 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_401] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 401", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3641,12 +3651,15 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_403] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 403", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3663,12 +3676,15 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_403FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 403 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3689,6 +3705,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_405] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 405", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3696,6 +3714,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3713,6 +3732,8 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_BEHOLD_405FM] = { /* Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 405 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3720,6 +3741,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3740,6 +3762,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_407] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 407", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3747,7 +3771,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0xc0c000, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3767,6 +3791,8 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_407FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 407 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3774,7 +3800,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0xc0c000, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3799,6 +3825,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_409] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 409", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3806,6 +3834,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3822,6 +3851,8 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_505FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 505 FM/RDS", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3829,6 +3860,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3853,6 +3885,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_507_9FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3860,6 +3894,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3880,6 +3915,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV Columbus TVFM", .audio_clock = 0x00187de7, .tuner_type = TUNER_ALPS_TSBE5_PAL, @@ -3887,23 +3924,28 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x000A8004, .inputs = {{ .name = name_tv, .vmux = 3, .amux = TV, .tv = 1, - },{ + .gpio = 0x000A8004, + }, { .name = name_comp1, .vmux = 1, .amux = LINE1, - },{ + .gpio = 0x000A8000, + }, { .name = name_svideo, .vmux = 8, .amux = LINE1, - }}, + .gpio = 0x000A8000, + } }, .radio = { .name = name_radio, .amux = LINE2, + .gpio = 0x000A8000, }, }, [SAA7134_BOARD_BEHOLD_607_9FM] = { @@ -4251,6 +4293,36 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, } }, }, + [SAA7134_BOARD_BEHOLD_H6] = { + /* Igor Kuznetsov <igk@igk.ru> */ + .name = "Beholder BeholdTV H6", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + /* no DVB support for now */ + /* .mpeg = SAA7134_MPEG_DVB, */ + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5201,6 +5273,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x5ace, .subdevice = 0x6193, .driver_data = SAA7134_BOARD_BEHOLD_M6, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6191, + .driver_data = SAA7134_BOARD_BEHOLD_M6, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5250,6 +5328,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0xc900, .driver_data = SAA7134_BOARD_VIDEOMATE_T750, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6290, + .driver_data = SAA7134_BOARD_BEHOLD_H6, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -5574,7 +5658,6 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_CARDBUS: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: case SAA7134_BOARD_AVERMEDIA_M115: - case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: case SAA7134_BOARD_AVERMEDIA_A16D: #if 1 /* power-down tuner chip */ @@ -5587,6 +5670,20 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); msleep(10); break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: +#if 1 + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0); +#endif + msleep(10); + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004); + msleep(10); + /* remote via GPIO */ + dev->has_remote = SAA7134_REMOTE_GPIO; + break; case SAA7134_BOARD_RTD_VFG7350: /* @@ -5632,20 +5729,87 @@ int saa7134_board_init1(struct saa7134_dev *dev) return 0; } +static void saa7134_tuner_setup(struct saa7134_dev *dev) +{ + struct tuner_setup tun_setup; + unsigned int mode_mask = T_RADIO | + T_ANALOG_TV | + T_DIGITAL_TV; + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.tuner_callback = saa7134_tuner_callback; + + if (saa7134_boards[dev->board].radio_type != UNSET) { + tun_setup.type = saa7134_boards[dev->board].radio_type; + tun_setup.addr = saa7134_boards[dev->board].radio_addr; + + tun_setup.mode_mask = T_RADIO; + + saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); + mode_mask &= ~T_RADIO; + } + + if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) { + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + tun_setup.config = saa7134_boards[dev->board].tuner_config; + tun_setup.tuner_callback = saa7134_tuner_callback; + + tun_setup.mode_mask = mode_mask; + + saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); + } + + if (dev->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + + saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, + &tda9887_cfg); + } + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(ctl)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.fname = XC2028_DEFAULT_FIRMWARE; + ctl.max_len = 64; + + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_A16D: + ctl.demod = XC3028_FE_ZARLINK456; + break; + default: + ctl.demod = XC3028_FE_OREN538; + ctl.mts = 1; + } + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); + } +} + /* stuff which needs working i2c */ int saa7134_board_init2(struct saa7134_dev *dev) { unsigned char buf; int board; - struct tuner_setup tun_setup; - tun_setup.config = 0; - tun_setup.tuner_callback = saa7134_tuner_callback; + + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tuner_addr = saa7134_boards[dev->board].tuner_addr; switch (dev->board) { case SAA7134_BOARD_BMK_MPEX_NOTUNER: case SAA7134_BOARD_BMK_MPEX_TUNER: dev->i2c_client.addr = 0x60; - board = (i2c_master_recv(&dev->i2c_client,&buf,0) < 0) + board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0) ? SAA7134_BOARD_BMK_MPEX_NOTUNER : SAA7134_BOARD_BMK_MPEX_TUNER; if (board == dev->board) @@ -5655,21 +5819,9 @@ int saa7134_board_init2(struct saa7134_dev *dev) saa7134_boards[dev->board].name); dev->tuner_type = saa7134_boards[dev->board].tuner_type; - if (TUNER_ABSENT != dev->tuner_type) { - tun_setup.mode_mask = T_RADIO | - T_ANALOG_TV | - T_DIGITAL_TV; - tun_setup.type = dev->tuner_type; - tun_setup.addr = ADDR_UNSET; - tun_setup.tuner_callback = saa7134_tuner_callback; - - saa7134_i2c_call_clients(dev, - TUNER_SET_TYPE_ADDR, - &tun_setup); - } break; case SAA7134_BOARD_MD7134: - { + { u8 subaddr; u8 data[3]; int ret, tuner_t; @@ -5722,30 +5874,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) } printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type); - if (dev->tuner_type == TUNER_PHILIPS_FMD1216ME_MK3) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - - dev->tda9887_conf = TDA9887_PRESENT | - TDA9887_PORT1_ACTIVE | - TDA9887_PORT2_ACTIVE; - - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); - } - - tun_setup.mode_mask = T_RADIO | - T_ANALOG_TV | - T_DIGITAL_TV; - tun_setup.type = dev->tuner_type; - tun_setup.addr = ADDR_UNSET; - - saa7134_i2c_call_clients(dev, - TUNER_SET_TYPE_ADDR, &tun_setup); - } break; + } case SAA7134_BOARD_PHILIPS_EUROPA: if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) { /* Reconfigure board as Snake reference design */ @@ -5757,43 +5887,43 @@ int saa7134_board_init2(struct saa7134_dev *dev) } case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: + { + /* The Philips EUROPA based hybrid boards have the tuner connected through * the channel decoder. We have to make it transparent to find it */ - { u8 data[] = { 0x07, 0x02}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); - } break; + } case SAA7134_BOARD_PHILIPS_TIGER: case SAA7134_BOARD_PHILIPS_TIGER_S: - { + { u8 data[] = { 0x3c, 0x33, 0x60}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - if(dev->autodetected && (dev->eedata[0x49] == 0x50)) { + if (dev->autodetected && (dev->eedata[0x49] == 0x50)) { dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; printk(KERN_INFO "%s: Reconfigured board as %s\n", dev->name, saa7134_boards[dev->board].name); } - if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; - tun_setup.type = TUNER_PHILIPS_TDA8290; - tun_setup.addr = 0x4b; - tun_setup.config = 2; + if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { + dev->tuner_type = TUNER_PHILIPS_TDA8290; + + saa7134_tuner_setup(dev); - saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); data[2] = 0x68; + i2c_transfer(&dev->i2c_adap, &msg, 1); + + /* Tuner setup is handled before I2C transfer. + Due to that, there's no need to do it later + */ + return 0; } i2c_transfer(&dev->i2c_adap, &msg, 1); - } break; + } case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); /* break intentionally omitted */ @@ -5806,52 +5936,55 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_SUPER_007: case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: case SAA7134_BOARD_CREATIX_CTX953: + { /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ - { u8 data[] = { 0x3c, 0x33, 0x60}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - } break; + } case SAA7134_BOARD_FLYDVB_TRIO: - { + { u8 data[] = { 0x3c, 0x33, 0x62}; struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - } break; + } case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: + { /* initialize analog mode */ - { u8 data[] = { 0x3c, 0x33, 0x6a}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - } break; + } case SAA7134_BOARD_CINERGY_HT_PCMCIA: case SAA7134_BOARD_CINERGY_HT_PCI: + { /* initialize analog mode */ - { u8 data[] = { 0x3c, 0x33, 0x68}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - } break; + } case SAA7134_BOARD_KWORLD_ATSC110: - { - /* enable tuner */ - int i; - static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 }; - dev->i2c_client.addr = 0x0a; - for (i = 0; i < 5; i++) - if (2 != i2c_master_send(&dev->i2c_client,&buffer[i*2],2)) - printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n", - dev->name, i); - } + { + /* enable tuner */ + int i; + static const u8 buffer [] = { 0x10, 0x12, 0x13, 0x04, 0x16, + 0x00, 0x14, 0x04, 0x17, 0x00 }; + dev->i2c_client.addr = 0x0a; + for (i = 0; i < 5; i++) + if (2 != i2c_master_send(&dev->i2c_client, + &buffer[i*2], 2)) + printk(KERN_WARNING + "%s: Unable to enable tuner(%i).\n", + dev->name, i); break; + } case SAA7134_BOARD_VIDEOMATE_DVBT_200: case SAA7134_BOARD_VIDEOMATE_DVBT_200A: /* The T200 and the T200A share the same pci id. Consequently, @@ -5875,32 +6008,23 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; } break; - } - - if (dev->tuner_type == TUNER_XC2028) { - struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + { + struct v4l2_priv_tun_config tea5767_cfg; + struct tea5767_ctrl ctl; - memset(&xc2028_cfg, 0, sizeof(ctl)); + dev->i2c_client.addr = 0xC0; + /* set TEA5767(analog FM) defines */ memset(&ctl, 0, sizeof(ctl)); - - ctl.fname = XC2028_DEFAULT_FIRMWARE; - ctl.max_len = 64; - - switch (dev->board) { - case SAA7134_BOARD_AVERMEDIA_A16D: - ctl.demod = XC3028_FE_ZARLINK456; - break; - default: - ctl.demod = XC3028_FE_OREN538; - ctl.mts = 1; - } - - xc2028_cfg.tuner = TUNER_XC2028; - xc2028_cfg.priv = &ctl; - - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); + ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; + tea5767_cfg.tuner = TUNER_TEA5767; + tea5767_cfg.priv = &ctl; + saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg); + break; } + } /* switch() */ + + saa7134_tuner_setup(dev); return 0; } diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index 7296eeba0..9ed2997f7 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -960,7 +960,6 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, struct saa7134_dev *dev; struct saa7134_mpeg_ops *mops; int err; - int mask; if (saa7134_devcount == SAA7134_MAXBOARDS) return -ENOMEM; @@ -1161,11 +1160,6 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (TUNER_ABSENT != dev->tuner_type) saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); - if (card(dev).gpiomask != 0) { - mask = card(dev).gpiomask; - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, 0); - } return 0; fail4: diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 19f5b1d34..2659d2a60 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -541,19 +541,23 @@ static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) return 0; } -static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *cdec_conf, - struct tda827x_config *tuner_conf) +static int configure_tda827x_fe(struct saa7134_dev *dev, + struct tda1004x_config *cdec_conf, + struct tda827x_config *tuner_conf) { dev->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); if (dev->dvb.frontend) { if (cdec_conf->i2c_gate) dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - if (dvb_attach(tda827x_attach, dev->dvb.frontend, cdec_conf->tuner_address, - &dev->i2c_adap, tuner_conf) == NULL) { - wprintk("no tda827x tuner found at addr: %02x\n", + if (dvb_attach(tda827x_attach, dev->dvb.frontend, + cdec_conf->tuner_address, + &dev->i2c_adap, tuner_conf)) + return 0; + + wprintk("no tda827x tuner found at addr: %02x\n", cdec_conf->tuner_address); - } } + return -EINVAL; } /* ------------------------------------------------------------------ */ @@ -854,6 +858,14 @@ static struct tda10086_config flydvbs = { .demod_address = 0x0e, .invert = 0, .diseqc_tone = 0, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct tda10086_config sd1878_4m = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 0, + .xtal_freq = TDA10086_XTAL_4M, }; /* ------------------------------------------------------------------ @@ -992,7 +1004,9 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: - configure_tda827x_fe(dev, &tda827x_lifeview_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &tda827x_lifeview_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_PHILIPS_EUROPA: case SAA7134_BOARD_VIDEOMATE_DVBT_300: @@ -1017,36 +1031,52 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_KWORLD_DVBT_210: - configure_tda827x_fe(dev, &kworld_dvb_t_210_config, &tda827x_cfg_2); + if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_PHILIPS_TIGER: - configure_tda827x_fe(dev, &philips_tiger_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &philips_tiger_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_PINNACLE_PCTV_310i: - configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, &tda827x_cfg_1); + if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, + &tda827x_cfg_1) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, &tda827x_cfg_1); + if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, + &tda827x_cfg_1) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - configure_tda827x_fe(dev, &asus_p7131_dual_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &asus_p7131_dual_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_FLYDVBT_LR301: - configure_tda827x_fe(dev, &tda827x_lifeview_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &tda827x_lifeview_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_FLYDVB_TRIO: - if(! use_frontend) { /* terrestrial */ - configure_tda827x_fe(dev, &lifeview_trio_config, &tda827x_cfg_0); + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &lifeview_trio_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; } else { /* satellite */ dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63, &dev->i2c_adap, 0) == NULL) { wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); + goto dettach_frontend; } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); + goto dettach_frontend; } } } @@ -1062,15 +1092,20 @@ static int dvb_init(struct saa7134_dev *dev) &ads_duo_cfg) == NULL) { wprintk("no tda827x tuner found at addr: %02x\n", ads_tech_duo_config.tuner_address); + goto dettach_frontend; } } break; case SAA7134_BOARD_TEVION_DVBT_220RF: - configure_tda827x_fe(dev, &tevion_dvbt220rf_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_MEDION_MD8800_QUADRO: if (!use_frontend) { /* terrestrial */ - configure_tda827x_fe(dev, &md8800_dvbt_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &md8800_dvbt_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; } else { /* satellite */ dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); @@ -1081,16 +1116,20 @@ static int dvb_init(struct saa7134_dev *dev) struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1}; if (dvb_attach(tda826x_attach, dev->dvb.frontend, - 0x60, &dev->i2c_adap, 0) == NULL) + 0x60, &dev->i2c_adap, 0) == NULL) { wprintk("%s: Medion Quadro, no tda826x " "found !\n", __func__); + goto dettach_frontend; + } if (dev_id != 0x08) { /* we need to open the i2c gate (we know it exists) */ fe->ops.i2c_gate_ctrl(fe, 1); if (dvb_attach(isl6405_attach, fe, - &dev->i2c_adap, 0x08, 0, 0) == NULL) + &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: Medion Quadro, no ISL6405 " "found !\n", __func__); + goto dettach_frontend; + } if (dev_id == 0x07) { /* fire up the 2nd section of the LNB supply since we can't do this from the other section */ @@ -1112,19 +1151,17 @@ static int dvb_init(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, &dev->i2c_adap); - if (dev->dvb.frontend) { + if (dev->dvb.frontend) dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, NULL, DVB_PLL_TDHU2); - } break; case SAA7134_BOARD_KWORLD_ATSC110: dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110, &dev->i2c_adap); - if (dev->dvb.frontend) { + if (dev->dvb.frontend) dvb_attach(simple_tuner_attach, dev->dvb.frontend, &dev->i2c_adap, 0x61, TUNER_PHILIPS_TUV1236D); - } break; case SAA7134_BOARD_FLYDVBS_LR300: dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, @@ -1133,10 +1170,12 @@ static int dvb_init(struct saa7134_dev *dev) if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { wprintk("%s: No tda826x found!\n", __func__); + goto dettach_frontend; } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: No ISL6421 found!\n", __func__); + goto dettach_frontend; } } break; @@ -1163,43 +1202,65 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: - configure_tda827x_fe(dev, &cinergy_ht_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &cinergy_ht_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_CINERGY_HT_PCI: - configure_tda827x_fe(dev, &cinergy_ht_pci_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &cinergy_ht_pci_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_PHILIPS_TIGER_S: - configure_tda827x_fe(dev, &philips_tiger_s_config, &tda827x_cfg_2); + if (configure_tda827x_fe(dev, &philips_tiger_s_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_ASUS_P7131_4871: - configure_tda827x_fe(dev, &asus_p7131_4871_config, &tda827x_cfg_2); + if (configure_tda827x_fe(dev, &asus_p7131_4871_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: - configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config, &tda827x_cfg_2); + if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_AVERMEDIA_SUPER_007: - configure_tda827x_fe(dev, &avermedia_super_007_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &avermedia_super_007_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: - configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config, &tda827x_cfg_2_sw42); + if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config, + &tda827x_cfg_2_sw42) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_PHILIPS_SNAKE: dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, - &dev->i2c_adap, 0) == NULL) + &dev->i2c_adap, 0) == NULL) { wprintk("%s: No tda826x found!\n", __func__); + goto dettach_frontend; + } if (dvb_attach(lnbp21_attach, dev->dvb.frontend, - &dev->i2c_adap, 0, 0) == NULL) + &dev->i2c_adap, 0, 0) == NULL) { wprintk("%s: No lnbp21 found!\n", __func__); + goto dettach_frontend; + } } break; case SAA7134_BOARD_CREATIX_CTX953: - configure_tda827x_fe(dev, &md8800_dvbt_config, &tda827x_cfg_0); + if (configure_tda827x_fe(dev, &md8800_dvbt_config, + &tda827x_cfg_0) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_MSI_TVANYWHERE_AD11: - configure_tda827x_fe(dev, &philips_tiger_s_config, &tda827x_cfg_2); + if (configure_tda827x_fe(dev, &philips_tiger_s_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: #if 0 @@ -1213,20 +1274,24 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_MD7134_BRIDGE_2: dev->dvb.frontend = dvb_attach(tda10086_attach, - &flydvbs, &dev->i2c_adap); + &sd1878_4m, &dev->i2c_adap); if (dev->dvb.frontend) { struct dvb_frontend *fe; if (dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, - &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) + &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) { wprintk("%s: MD7134 DVB-S, no SD1878 " "found !\n", __func__); + goto dettach_frontend; + } /* we need to open the i2c gate (we know it exists) */ fe = dev->dvb.frontend; fe->ops.i2c_gate_ctrl(fe, 1); if (dvb_attach(isl6405_attach, fe, - &dev->i2c_adap, 0x08, 0, 0) == NULL) + &dev->i2c_adap, 0x08, 0, 0) == NULL) { wprintk("%s: MD7134 DVB-S, no ISL6405 " "found !\n", __func__); + goto dettach_frontend; + } fe->ops.i2c_gate_ctrl(fe, 0); dev->original_set_voltage = fe->ops.set_voltage; fe->ops.set_voltage = md8800_set_voltage; @@ -1253,10 +1318,7 @@ static int dvb_init(struct saa7134_dev *dev) if (!fe) { printk(KERN_ERR "%s/2: xc3028 attach failed\n", dev->name); - dvb_frontend_detach(dev->dvb.frontend); - dvb_unregister_frontend(dev->dvb.frontend); - dev->dvb.frontend = NULL; - return -1; + goto dettach_frontend; } } @@ -1281,6 +1343,12 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.sleep(dev->dvb.frontend); } return ret; + +dettach_frontend: + dvb_frontend_detach(dev->dvb.frontend); + dev->dvb.frontend = NULL; + + return -1; } static int dvb_fini(struct saa7134_dev *dev) diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index fda6d44cd..c222313fc 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -140,6 +140,8 @@ static inline int i2c_is_busy(enum i2c_status status) { switch (status) { case BUSY: + case TO_SCL: + case TO_ARB: return true; default: return false; @@ -327,8 +329,6 @@ static u32 functionality(struct i2c_adapter *adap) static int attach_inform(struct i2c_client *client) { struct saa7134_dev *dev = client->adapter->algo_data; - int tuner = dev->tuner_type; - struct tuner_setup tun_setup; d1printk( "%s i2c attach [addr=0x%x,client=%s]\n", #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) @@ -357,46 +357,6 @@ static int attach_inform(struct i2c_client *client) } } - if (!client->driver->command) - return 0; - - if (saa7134_boards[dev->board].radio_type != UNSET) { - - tun_setup.type = saa7134_boards[dev->board].radio_type; - tun_setup.addr = saa7134_boards[dev->board].radio_addr; - - if ((tun_setup.addr == ADDR_UNSET) || (tun_setup.addr == client->addr)) { - tun_setup.mode_mask = T_RADIO; - - client->driver->command(client, TUNER_SET_TYPE_ADDR, &tun_setup); - } - } - - if (tuner != UNSET) { - tun_setup.type = tuner; - tun_setup.addr = saa7134_boards[dev->board].tuner_addr; - tun_setup.config = saa7134_boards[dev->board].tuner_config; - tun_setup.tuner_callback = saa7134_tuner_callback; - - if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) { - - tun_setup.mode_mask = T_ANALOG_TV; - - client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_setup); - } - - if (tuner == TUNER_TDA9887) { - struct v4l2_priv_tun_config tda9887_cfg; - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - - client->driver->command(client, TUNER_SET_CONFIG, - &tda9887_cfg); - } - } - - return 0; } diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 225f075e5..45c4c8125 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -331,6 +331,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: + ir_codes = ir_codes_manli; + mask_keycode = 0x001f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; case SAA7134_BOARD_BEHOLD_409FM: case SAA7134_BOARD_BEHOLD_401: case SAA7134_BOARD_BEHOLD_403: @@ -343,7 +348,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_505FM: case SAA7134_BOARD_BEHOLD_507_9FM: ir_codes = ir_codes_manli; - mask_keycode = 0x001f00; + mask_keycode = 0x003f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + ir_codes = ir_codes_behold_columbus; + mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; // ms break; @@ -530,6 +541,7 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) break; case SAA7134_BOARD_BEHOLD_607_9FM: case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_H6: snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV"); ir->get_key = get_key_beholdm6xx; ir->ir_codes = ir_codes_behold; diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 3b7774305..b8444429a 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -1641,7 +1641,7 @@ static int saa7134_s_fmt_overlay(struct file *file, void *priv, struct saa7134_fh *fh = priv; struct saa7134_dev *dev = fh->dev; int err; - unsigned int flags; + unsigned long flags; if (saa7134_no_overlay > 0) { printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index c8fb28d99..2aa30895f 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -270,6 +270,7 @@ struct saa7134_format { #define SAA7134_BOARD_VIDEOMATE_T750 139 #define SAA7134_BOARD_AVERMEDIA_A700_PRO 140 #define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141 +#define SAA7134_BOARD_BEHOLD_H6 142 #define SAA7134_MAXBOARDS 8 diff --git a/linux/drivers/media/video/saa717x.c b/linux/drivers/media/video/saa717x.c index bdae62189..98e0ec245 100644 --- a/linux/drivers/media/video/saa717x.c +++ b/linux/drivers/media/video/saa717x.c @@ -1431,7 +1431,8 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) /* i2c implementation */ /* ----------------------------------------------------------------------- */ -static int saa717x_probe(struct i2c_client *client) +static int saa717x_probe(struct i2c_client *client, + const struct i2c_device_id *did) { struct saa717x_state *decoder; u8 id = 0; diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 91a1251d9..a1b92446c 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -144,8 +144,6 @@ static int soc_camera_reqbufs(struct file *file, void *priv, return ret; return ici->ops->reqbufs(icf, p); - - return ret; } static int soc_camera_querybuf(struct file *file, void *priv, diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c index 7d8843c8e..f7f04aea7 100644 --- a/linux/drivers/media/video/stk-webcam.c +++ b/linux/drivers/media/video/stk-webcam.c @@ -30,6 +30,7 @@ #include <linux/kref.h> #include <linux/usb.h> +#include <linux/mm.h> #include <linux/vmalloc.h> #include "compat.h" #include <linux/videodev2.h> @@ -246,6 +247,8 @@ static int stk_initialise(struct stk_camera *dev) return -1; } +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /* sysfs functions */ /*FIXME cleanup this */ @@ -351,6 +354,10 @@ static void stk_remove_sysfs_files(struct video_device *vdev) video_device_remove_file(vdev, &dev_attr_vflip); } +#else +#define stk_create_sysfs_files(a) +#define stk_remove_sysfs_files(a) +#endif /* *********************************************** */ /* diff --git a/linux/drivers/media/video/tcm825x.c b/linux/drivers/media/video/tcm825x.c index 6943b447a..216638e00 100644 --- a/linux/drivers/media/video/tcm825x.c +++ b/linux/drivers/media/video/tcm825x.c @@ -523,6 +523,9 @@ static int ioctl_g_ctrl(struct v4l2_int_device *s, if (val < 0) return val; + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + vc->value = val; return 0; } @@ -556,6 +559,9 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, if (lvc == NULL) return -EINVAL; + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + val = val << lvc->start_bit; if (tcm825x_write_reg_mask(client, lvc->reg, val)) return -EIO; @@ -840,7 +846,8 @@ static struct v4l2_int_device tcm825x_int_device = { }, }; -static int tcm825x_probe(struct i2c_client *client) +static int tcm825x_probe(struct i2c_client *client, + const struct i2c_device_id *did) { struct tcm825x_sensor *sensor = &tcm825x; int rval; diff --git a/linux/drivers/media/video/tcm825x.h b/linux/drivers/media/video/tcm825x.h index 966765b66..770ebacfa 100644 --- a/linux/drivers/media/video/tcm825x.h +++ b/linux/drivers/media/video/tcm825x.h @@ -182,6 +182,7 @@ struct tcm825x_platform_data { int (*needs_reset)(struct v4l2_int_device *s, void *buf, struct v4l2_pix_format *fmt); int (*ifparm)(struct v4l2_ifparm *p); + int (*is_upside_down)(void); }; /* Array of image sizes supported by TCM825X. These must be ordered from diff --git a/linux/drivers/media/video/tlv320aic23b.c b/linux/drivers/media/video/tlv320aic23b.c index 29e1f8a7b..a7453ed6d 100644 --- a/linux/drivers/media/video/tlv320aic23b.c +++ b/linux/drivers/media/video/tlv320aic23b.c @@ -133,7 +133,8 @@ static int tlv320aic23b_command(struct i2c_client *client, * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int tlv320aic23b_probe(struct i2c_client *client) +static int tlv320aic23b_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct tlv320aic23b_state *state; diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index fa218e910..314d89cef 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -41,6 +41,46 @@ #define PREFIX t->i2c->driver->driver.name #endif +/** This macro allows us to probe dynamically, avoiding static links */ +#ifdef CONFIG_MEDIA_ATTACH +#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ + int __r = -EINVAL; \ + typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ + if (__a) { \ + __r = (int) __a(ARGS); \ + symbol_put(FUNCTION); \ + } else { \ + printk(KERN_ERR "TUNER: Unable to find " \ + "symbol "#FUNCTION"()\n"); \ + } \ + __r; \ +}) + +static void tuner_detach(struct dvb_frontend *fe) +{ + if (fe->ops.tuner_ops.release) { + fe->ops.tuner_ops.release(fe); + symbol_put_addr(fe->ops.tuner_ops.release); + } + if (fe->ops.analog_ops.release) { + fe->ops.analog_ops.release(fe); + symbol_put_addr(fe->ops.analog_ops.release); + } +} +#else +#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ + FUNCTION(ARGS); \ +}) + +static void tuner_detach(struct dvb_frontend *fe) +{ + if (fe->ops.tuner_ops.release) + fe->ops.tuner_ops.release(fe); + if (fe->ops.analog_ops.release) + fe->ops.analog_ops.release(fe); +} +#endif + struct tuner { /* device */ struct dvb_frontend fe; @@ -64,7 +104,7 @@ struct tuner { /* standard i2c insmod options */ static unsigned short normal_i2c[] = { -#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE)) +#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) 0x10, #endif 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ @@ -168,22 +208,6 @@ static void fe_set_params(struct dvb_frontend *fe, fe_tuner_ops->set_analog_params(fe, params); } -static void fe_release(struct dvb_frontend *fe) -{ - if (fe->ops.tuner_ops.release) - fe->ops.tuner_ops.release(fe); - - /* DO NOT kfree(fe->analog_demod_priv) - * - * If we are in this function, analog_demod_priv contains a pointer - * to struct tuner *t. This will be kfree'd in tuner_detach(). - * - * Otherwise, fe->ops.analog_demod_ops->release will - * handle the cleanup for analog demodulator modules. - */ - fe->analog_demod_priv = NULL; -} - static void fe_standby(struct dvb_frontend *fe) { struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; @@ -220,7 +244,6 @@ static void tuner_status(struct dvb_frontend *fe); static struct analog_demod_ops tuner_core_ops = { .set_params = fe_set_params, .standby = fe_standby, - .release = fe_release, .has_signal = fe_has_signal, .set_config = fe_set_config, .tuner_status = tuner_status @@ -346,15 +369,6 @@ static void tuner_i2c_address_check(struct tuner *t) tuner_warn("====================== WARNING! ======================\n"); } -static void attach_tda829x(struct tuner *t) -{ - struct tda829x_config cfg = { - .lna_cfg = t->config, - .tuner_callback = t->tuner_callback, - }; - tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg); -} - static struct xc5000_config xc5000_cfg; static void set_type(struct i2c_client *c, unsigned int type, @@ -385,32 +399,36 @@ static void set_type(struct i2c_client *c, unsigned int type, } /* discard private data, in case set_type() was previously called */ - if (analog_ops->release) - analog_ops->release(&t->fe); + tuner_detach(&t->fe); + t->fe.analog_demod_priv = NULL; switch (t->type) { case TUNER_MT2032: - microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr); + if (!dvb_attach(microtune_attach, + &t->fe, t->i2c->adapter, t->i2c->addr)) + goto attach_failed; break; case TUNER_PHILIPS_TDA8290: { - attach_tda829x(t); + struct tda829x_config cfg = { + .lna_cfg = t->config, + .tuner_callback = t->tuner_callback, + }; + if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter, + t->i2c->addr, &cfg)) + goto attach_failed; break; } case TUNER_TEA5767: - if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } + if (!dvb_attach(tea5767_attach, &t->fe, + t->i2c->adapter, t->i2c->addr)) + goto attach_failed; t->mode_mask = T_RADIO; break; case TUNER_TEA5761: - if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } + if (!dvb_attach(tea5761_attach, &t->fe, + t->i2c->adapter, t->i2c->addr)) + goto attach_failed; t->mode_mask = T_RADIO; break; case TUNER_PHILIPS_FMD1216ME_MK3: @@ -423,25 +441,19 @@ static void set_type(struct i2c_client *c, unsigned int type, buffer[2] = 0x86; buffer[3] = 0x54; i2c_master_send(c, buffer, 4); - if (simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, - t->type) == NULL) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } + if (!dvb_attach(simple_tuner_attach, &t->fe, + t->i2c->adapter, t->i2c->addr, t->type)) + goto attach_failed; break; case TUNER_PHILIPS_TD1316: buffer[0] = 0x0b; buffer[1] = 0xdc; buffer[2] = 0x86; buffer[3] = 0xa4; - i2c_master_send(c,buffer,4); - if (simple_tuner_attach(&t->fe, t->i2c->adapter, - t->i2c->addr, t->type) == NULL) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } + i2c_master_send(c, buffer, 4); + if (!dvb_attach(simple_tuner_attach, &t->fe, + t->i2c->adapter, t->i2c->addr, t->type)) + goto attach_failed; break; case TUNER_XC2028: { @@ -450,51 +462,50 @@ static void set_type(struct i2c_client *c, unsigned int type, .i2c_addr = t->i2c->addr, .callback = t->tuner_callback, }; - if (!xc2028_attach(&t->fe, &cfg)) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } + if (!dvb_attach(xc2028_attach, &t->fe, &cfg)) + goto attach_failed; break; } case TUNER_TDA9887: - tda9887_attach(&t->fe, t->i2c->adapter, t->i2c->addr); + if (!dvb_attach(tda9887_attach, + &t->fe, t->i2c->adapter, t->i2c->addr)) + goto attach_failed; break; case TUNER_XC5000: + { + struct dvb_tuner_ops *xc_tuner_ops; + xc5000_cfg.i2c_address = t->i2c->addr; xc5000_cfg.if_khz = 5380; - xc5000_cfg.priv = c->adapter->algo_data; xc5000_cfg.tuner_callback = t->tuner_callback; - if (!xc5000_attach(&t->fe, t->i2c->adapter, &xc5000_cfg)) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } - { - struct dvb_tuner_ops *xc_tuner_ops; + if (!dvb_attach(xc5000_attach, + &t->fe, t->i2c->adapter, &xc5000_cfg, + c->adapter->algo_data)) + goto attach_failed; + xc_tuner_ops = &t->fe.ops.tuner_ops; - if(xc_tuner_ops->init != NULL) + if (xc_tuner_ops->init) xc_tuner_ops->init(&t->fe); - } break; + } default: - if (simple_tuner_attach(&t->fe, t->i2c->adapter, - t->i2c->addr, t->type) == NULL) { - t->type = TUNER_ABSENT; - t->mode_mask = T_UNINITIALIZED; - return; - } + if (!dvb_attach(simple_tuner_attach, &t->fe, + t->i2c->adapter, t->i2c->addr, t->type)) + goto attach_failed; + break; } if ((NULL == analog_ops->set_params) && (fe_tuner_ops->set_analog_params)) { + strlcpy(t->i2c->name, fe_tuner_ops->info.name, sizeof(t->i2c->name)); t->fe.analog_demod_priv = t; memcpy(analog_ops, &tuner_core_ops, sizeof(struct analog_demod_ops)); + } else { strlcpy(t->i2c->name, analog_ops->info.name, sizeof(t->i2c->name)); @@ -505,7 +516,15 @@ static void set_type(struct i2c_client *c, unsigned int type, if (t->mode_mask == T_UNINITIALIZED) t->mode_mask = new_mode_mask; - set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq); + /* xc2028/3028 and xc5000 requires a firmware to be set-up later + trying to set a frequency here will just fail + FIXME: better to move set_freq to the tuner code. This is needed + on analog tuners for PLL to properly work + */ + if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000) + set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? + t->radio_freq : t->tv_freq); + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", c->adapter->name, c->driver->name, c->addr << 1, type, @@ -516,6 +535,14 @@ static void set_type(struct i2c_client *c, unsigned int type, t->mode_mask); #endif tuner_i2c_address_check(t); + return; + +attach_failed: + tuner_dbg("Tuner attach for type = %d failed.\n", t->type); + t->type = TUNER_ABSENT; + t->mode_mask = T_UNINITIALIZED; + + return; } /* @@ -530,14 +557,16 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) { struct tuner *t = i2c_get_clientdata(c); - tuner_dbg("set addr for type %i\n", t->type); - if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && (t->mode_mask & tun_setup->mode_mask))) || (tun_setup->addr == c->addr)) { set_type(c, tun_setup->type, tun_setup->mode_mask, tun_setup->config, tun_setup->tuner_callback); - } + } else + tuner_dbg("set addr discarded for type %i, mask %x. " + "Asked to change tuner at addr 0x%02x, with mask %x\n", + t->type, t->mode_mask, + tun_setup->addr, tun_setup->mode_mask); } static inline int check_mode(struct tuner *t, char *cmd) @@ -546,7 +575,7 @@ static inline int check_mode(struct tuner *t, char *cmd) #if 0 tuner_dbg("Cmd %s rejected for mode %i\n", cmd,t->mode); #endif - return EINVAL; + return -EINVAL; } switch (t->mode) { @@ -683,8 +712,8 @@ static void tuner_status(struct dvb_frontend *fe) { struct tuner *t = fe->analog_demod_priv; unsigned long freq, freq_fraction; - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; + struct analog_demod_ops *analog_ops = &fe->ops.analog_ops; const char *p; switch (t->mode) { @@ -740,11 +769,11 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, t->mode = mode; - if (check_mode(t, cmd) == EINVAL) { + if (check_mode(t, cmd) == -EINVAL) { t->mode = T_STANDBY; if (analog_ops->standby) analog_ops->standby(&t->fe); - return EINVAL; + return -EINVAL; } return 0; } @@ -774,8 +803,10 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (tuner_debug>1) + if (tuner_debug > 1) { v4l_i2c_print_ioctl(client,cmd); + printk("\n"); + } switch (cmd) { /* --- configuration --- */ @@ -790,13 +821,13 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case AUDC_SET_RADIO: if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") - == EINVAL) + == -EINVAL) return 0; if (t->radio_freq) set_freq(client, t->radio_freq); break; case TUNER_SET_STANDBY: - if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL) + if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -804,9 +835,9 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; #ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCSAUDIO: - if (check_mode(t, "VIDIOCSAUDIO") == EINVAL) + if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL) return 0; - if (check_v4l2(t) == EINVAL) + if (check_v4l2(t) == -EINVAL) return 0; /* Should be implemented, since bttv calls it */ @@ -824,10 +855,10 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) }; struct video_channel *vc = arg; - if (check_v4l2(t) == EINVAL) + if (check_v4l2(t) == -EINVAL) return 0; - if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL) + if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==-EINVAL) return 0; if (vc->norm < ARRAY_SIZE(map)) @@ -841,9 +872,9 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { unsigned long *v = arg; - if (check_mode(t, "VIDIOCSFREQ") == EINVAL) + if (check_mode(t, "VIDIOCSFREQ") == -EINVAL) return 0; - if (check_v4l2(t) == EINVAL) + if (check_v4l2(t) == -EINVAL) return 0; set_freq(client, *v); @@ -853,9 +884,9 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct video_tuner *vt = arg; - if (check_mode(t, "VIDIOCGTUNER") == EINVAL) + if (check_mode(t, "VIDIOCGTUNER") == -EINVAL) return 0; - if (check_v4l2(t) == EINVAL) + if (check_v4l2(t) == -EINVAL) return 0; if (V4L2_TUNER_RADIO == t->mode) { @@ -897,9 +928,9 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct video_audio *va = arg; - if (check_mode(t, "VIDIOCGAUDIO") == EINVAL) + if (check_mode(t, "VIDIOCGAUDIO") == -EINVAL) return 0; - if (check_v4l2(t) == EINVAL) + if (check_v4l2(t) == -EINVAL) return 0; if (V4L2_TUNER_RADIO == t->mode) { @@ -939,7 +970,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) v4l2_std_id *id = arg; if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") - == EINVAL) + == -EINVAL) return 0; switch_v4l2(); @@ -955,7 +986,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) struct v4l2_frequency *f = arg; if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY") - == EINVAL) + == -EINVAL) return 0; switch_v4l2(); set_freq(client,f->frequency); @@ -966,7 +997,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_frequency *f = arg; - if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL) + if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) return 0; switch_v4l2(); f->type = t->mode; @@ -987,7 +1018,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_tuner *tuner = arg; - if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL) + if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) return 0; switch_v4l2(); @@ -1034,7 +1065,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_tuner *tuner = arg; - if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL) + if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) return 0; switch_v4l2(); @@ -1117,7 +1148,8 @@ static void tuner_lookup(struct i2c_adapter *adap, /* During client attach, set_type is called by adapter's attach_inform callback. set_type must then be completed by tuner_probe. */ -static int tuner_probe(struct i2c_client *client) +static int tuner_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct tuner *t; struct tuner *radio; @@ -1155,8 +1187,9 @@ static int tuner_probe(struct i2c_client *client) if (!no_autodetect) { switch (client->addr) { case 0x10: - if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr) - != EINVAL) { + if (tuner_symbol_probe(tea5761_autodetection, + t->i2c->adapter, + t->i2c->addr) >= 0) { t->type = TUNER_TEA5761; t->mode_mask = T_RADIO; t->mode = T_STANDBY; @@ -1168,15 +1201,15 @@ static int tuner_probe(struct i2c_client *client) goto register_client; } - break; + return -ENODEV; case 0x42: case 0x43: case 0x4a: case 0x4b: /* If chip is not tda8290, don't register. since it can be tda9887*/ - if (tda829x_probe(t->i2c->adapter, - t->i2c->addr) == 0) { + if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter, + t->i2c->addr) >= 0) { tuner_dbg("tda829x detected\n"); } else { /* Default is being tda9887 */ @@ -1188,8 +1221,9 @@ static int tuner_probe(struct i2c_client *client) } break; case 0x60: - if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr) - != EINVAL) { + if (tuner_symbol_probe(tea5767_autodetection, + t->i2c->adapter, t->i2c->addr) + >= 0) { t->type = TUNER_TEA5767; t->mode_mask = T_RADIO; t->mode = T_STANDBY; @@ -1281,10 +1315,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap) static int tuner_remove(struct i2c_client *client) { struct tuner *t = i2c_get_clientdata(client); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (analog_ops->release) - analog_ops->release(&t->fe); + tuner_detach(&t->fe); + t->fe.analog_demod_priv = NULL; list_del(&t->list); kfree(t); diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 9ebe13fd7..7b7cfa3b6 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1478,7 +1478,7 @@ static struct CHIPDESC chiplist[] = { /* ---------------------------------------------------------------------- */ /* i2c registration */ -static int chip_probe(struct i2c_client *client) +static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct CHIPSTATE *chip; struct CHIPDESC *desc; diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index 81a85035f..e38bda67a 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -323,10 +323,12 @@ audioIC[] = {AUDIO_CHIP_INTERNAL, "CX25843"}, {AUDIO_CHIP_INTERNAL, "CX23418"}, {AUDIO_CHIP_INTERNAL, "CX23885"}, - /* 40-42 */ + /* 40-44 */ {AUDIO_CHIP_INTERNAL, "CX23888"}, {AUDIO_CHIP_INTERNAL, "SAA7131"}, {AUDIO_CHIP_INTERNAL, "CX23887"}, + {AUDIO_CHIP_INTERNAL, "SAA7164"}, + {AUDIO_CHIP_INTERNAL, "AU8522"}, }; /* This list is supplied by Hauppauge. Thanks! */ @@ -345,8 +347,10 @@ static const char *decoderIC[] = { "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", /* 30-34 */ "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", - /* 35-37 */ - "SAA7131", "CX25837", "CX23887" + /* 35-39 */ + "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A", + /* 40-42 */ + "SAA7164", "CX23885B", "AU8522" }; static int hasRadioTuner(int tunerType) diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c index 92fc10901..5408309cb 100644 --- a/linux/drivers/media/video/upd64031a.c +++ b/linux/drivers/media/video/upd64031a.c @@ -209,7 +209,8 @@ static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg) /* i2c implementation */ -static int upd64031a_probe(struct i2c_client *client) +static int upd64031a_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct upd64031a_state *state; int i; diff --git a/linux/drivers/media/video/upd64083.c b/linux/drivers/media/video/upd64083.c index 5d441538f..e43cc103f 100644 --- a/linux/drivers/media/video/upd64083.c +++ b/linux/drivers/media/video/upd64083.c @@ -186,7 +186,8 @@ static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) /* i2c implementation */ -static int upd64083_probe(struct i2c_client *client) +static int upd64083_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct upd64083_state *state; int i; diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index 5853563fa..049f588ae 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -72,12 +72,6 @@ #define VICAM_HEADER_SIZE 64 -#define clamp( x, l, h ) max_t( __typeof__( x ), \ - ( l ), \ - min_t( __typeof__( x ), \ - ( h ), \ - ( x ) ) ) - /* Not sure what all the bytes in these char * arrays do, but they're necessary to make * the camera work. diff --git a/linux/drivers/media/video/usbvision/Makefile b/linux/drivers/media/video/usbvision/Makefile index 9ac92a80c..338718750 100644 --- a/linux/drivers/media/video/usbvision/Makefile +++ b/linux/drivers/media/video/usbvision/Makefile @@ -3,3 +3,4 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision- obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index 34079657e..b721aca4e 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -711,13 +711,14 @@ EXPORT_SYMBOL(v4l2_chip_ident_i2c_client); /* Helper function for I2C legacy drivers */ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver, - const char *name, int (*probe)(struct i2c_client *)) + const char *name, + int (*probe)(struct i2c_client *, const struct i2c_device_id *)) { struct i2c_client *client; int err; client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + if (!client) return -ENOMEM; client->addr = address; @@ -728,7 +729,7 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver #endif strlcpy(client->name, name, sizeof(client->name)); - err = probe(client); + err = probe(client, NULL); if (err == 0) { i2c_attach_client(client); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) diff --git a/linux/drivers/media/video/videobuf-core.c b/linux/drivers/media/video/videobuf-core.c index 367f150a9..16c32c68c 100644 --- a/linux/drivers/media/video/videobuf-core.c +++ b/linux/drivers/media/video/videobuf-core.c @@ -98,7 +98,10 @@ int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, void *videobuf_queue_to_vmalloc (struct videobuf_queue *q, struct videobuf_buffer *buf) { - return CALL(q, vmalloc, buf); + if (q->int_ops->vmalloc) + return q->int_ops->vmalloc(buf); + else + return NULL; } EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); @@ -131,12 +134,12 @@ void videobuf_queue_core_init(struct videobuf_queue *q, BUG_ON(!q->ops->buf_queue); BUG_ON(!q->ops->buf_release); + /* Lock is mandatory for queue_cancel to work */ + BUG_ON(!irqlock); + /* Having implementations for abstract methods are mandatory */ BUG_ON(!q->int_ops); - /* Having irqlock is mandatory */ - BUG_ON(!q->irqlock); - mutex_init(&q->vb_lock); init_waitqueue_head(&q->wait); INIT_LIST_HEAD(&q->stream); @@ -329,7 +332,7 @@ int videobuf_mmap_free(struct videobuf_queue *q) } /* Locking: Caller holds q->vb_lock */ -static int __videobuf_mmap_setup(struct videobuf_queue *q, +int __videobuf_mmap_setup(struct videobuf_queue *q, unsigned int bcount, unsigned int bsize, enum v4l2_memory memory) { @@ -759,7 +762,6 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); spin_unlock_irqrestore(q->irqlock, flags); - retval = videobuf_waiton(q->read_buf, 0, 0); if (0 == retval) { CALL(q, sync, q, q->read_buf); @@ -820,6 +822,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, q->read_buf = NULL; goto done; } + spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); spin_unlock_irqrestore(q->irqlock, flags); @@ -889,12 +892,10 @@ static int __videobuf_read_start(struct videobuf_queue *q) return err; list_add_tail(&q->bufs[i]->stream, &q->stream); } - spin_lock_irqsave(q->irqlock, flags); for (i = 0; i < count; i++) q->ops->buf_queue(q, q->bufs[i]); spin_unlock_irqrestore(q->irqlock, flags); - q->reading = 1; return 0; } @@ -1006,11 +1007,9 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, if (q->read_off == q->read_buf->size) { list_add_tail(&q->read_buf->stream, &q->stream); - spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); spin_unlock_irqrestore(q->irqlock, flags); - q->read_buf = NULL; } if (retval < 0) @@ -1131,6 +1130,7 @@ EXPORT_SYMBOL_GPL(videobuf_read_stream); EXPORT_SYMBOL_GPL(videobuf_read_one); EXPORT_SYMBOL_GPL(videobuf_poll_stream); +EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); EXPORT_SYMBOL_GPL(videobuf_mmap_setup); EXPORT_SYMBOL_GPL(videobuf_mmap_free); EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); diff --git a/linux/drivers/media/video/videodev.c b/linux/drivers/media/video/videodev.c index 31e28a14f..684c7e4b9 100644 --- a/linux/drivers/media/video/videodev.c +++ b/linux/drivers/media/video/videodev.c @@ -335,6 +335,7 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", + [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -1898,6 +1899,24 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); break; } + default: + { + if (!vfd->vidioc_default) + break; + ret = vfd->vidioc_default(file, fh, cmd, arg); + break; + } + case VIDIOC_S_HW_FREQ_SEEK: + { + struct v4l2_hw_freq_seek *p = arg; + if (!vfd->vidioc_s_hw_freq_seek) + break; + dbgarg(cmd, + "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n", + p->tuner, p->type, p->seek_upward, p->wrap_around); + ret = vfd->vidioc_s_hw_freq_seek(file, fh, p); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index ef7752aa8..bf15e2eb2 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -13,7 +13,7 @@ /* * TODO: * - remove "mark pages reserved-hacks" from memory allocation code - * and implement nopage() + * and implement fault() * - check decimation, calculating and reporting image size when * using decimation * - implement read(), user mode buffers and overlay (?) diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 900a17404..d190754f1 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -958,7 +958,7 @@ static int vivi_open(struct inode *inode, struct file *file) { int minor = iminor(inode); struct vivi_dev *dev; - struct vivi_fh *fh; + struct vivi_fh *fh = NULL; int i; int retval = 0; diff --git a/linux/drivers/media/video/vp27smpx.c b/linux/drivers/media/video/vp27smpx.c index 922aa8d74..28f0cd162 100644 --- a/linux/drivers/media/video/vp27smpx.c +++ b/linux/drivers/media/video/vp27smpx.c @@ -135,7 +135,8 @@ static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int vp27smpx_probe(struct i2c_client *client) +static int vp27smpx_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct vp27smpx_state *state; diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c index f2010b8a7..838444ece 100644 --- a/linux/drivers/media/video/wm8739.c +++ b/linux/drivers/media/video/wm8739.c @@ -279,7 +279,8 @@ static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) /* i2c implementation */ -static int wm8739_probe(struct i2c_client *client) +static int wm8739_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct wm8739_state *state; diff --git a/linux/drivers/media/video/wm8775.c b/linux/drivers/media/video/wm8775.c index b0030789b..d0e47fb22 100644 --- a/linux/drivers/media/video/wm8775.c +++ b/linux/drivers/media/video/wm8775.c @@ -167,7 +167,8 @@ static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int wm8775_probe(struct i2c_client *client) +static int wm8775_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct wm8775_state *state; diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c index 6b7a7c137..0949c53f0 100644 --- a/linux/drivers/media/video/zoran_driver.c +++ b/linux/drivers/media/video/zoran_driver.c @@ -1234,7 +1234,7 @@ zoran_close_end_session (struct file *file) /* v4l capture */ if (fh->v4l_buffers.active != ZORAN_FREE) { - long flags; + unsigned long flags; spin_lock_irqsave(&zr->spinlock, flags); zr36057_set_memgrab(zr, 0); @@ -3503,7 +3503,7 @@ zoran_do_ioctl (struct inode *inode, /* unload capture */ if (zr->v4l_memgrab_active) { - long flags; + unsigned long flags; spin_lock_irqsave(&zr->spinlock, flags); zr36057_set_memgrab(zr, 0); @@ -4442,7 +4442,7 @@ zoran_vm_close (struct vm_area_struct *vma) mutex_lock(&zr->resource_lock); if (fh->v4l_buffers.active != ZORAN_FREE) { - long flags; + unsigned long flags; spin_lock_irqsave(&zr->spinlock, flags); zr36057_set_memgrab(zr, 0); diff --git a/linux/drivers/media/video/zoran_procfs.c b/linux/drivers/media/video/zoran_procfs.c index 5aa285bdc..d7bc097da 100644 --- a/linux/drivers/media/video/zoran_procfs.c +++ b/linux/drivers/media/video/zoran_procfs.c @@ -189,6 +189,7 @@ static ssize_t zoran_write(struct file *file, const char __user *buffer, } static const struct file_operations zoran_operations = { + .owner = THIS_MODULE, .open = zoran_open, .read = seq_read, .write = zoran_write, @@ -204,10 +205,8 @@ zoran_proc_init (struct zoran *zr) char name[8]; snprintf(name, 7, "zoran%d", zr->id); - if ((zr->zoran_proc = create_proc_entry(name, 0, NULL))) { - zr->zoran_proc->data = zr; - zr->zoran_proc->owner = THIS_MODULE; - zr->zoran_proc->proc_fops = &zoran_operations; + zr->zoran_proc = proc_create_data(name, 0, NULL, &zoran_operations, zr); + if (zr->zoran_proc != NULL) { dprintk(2, KERN_INFO "%s: procfs entry /proc/%s allocated. data=%p\n", diff --git a/linux/include/asm-arm/arch-pxa/pxa-regs.h b/linux/include/asm-arm/arch-pxa/pxa-regs.h index 368b2297c..4b2ea1e95 100644 --- a/linux/include/asm-arm/arch-pxa/pxa-regs.h +++ b/linux/include/asm-arm/arch-pxa/pxa-regs.h @@ -46,13 +46,13 @@ #define PCMCIA1AttrSp PCMCIAAttrSp /* PCMCIA 1 Attribute Space [byte] */ #define PCMCIA1MemSp PCMCIAMemSp /* PCMCIA 1 Memory Space [byte] */ -#define _PCMCIA(Nb) /* PCMCIA [0..1] */ \ - (0x20000000 + (Nb)*PCMCIASp) +#define _PCMCIA(Nb) /* PCMCIA [0..1] */ \ + (0x20000000 + (Nb)*PCMCIASp) #define _PCMCIAIO(Nb) _PCMCIA (Nb) /* PCMCIA I/O [0..1] */ -#define _PCMCIAAttr(Nb) /* PCMCIA Attribute [0..1] */ \ - (_PCMCIA (Nb) + 2*PCMCIAPrtSp) -#define _PCMCIAMem(Nb) /* PCMCIA Memory [0..1] */ \ - (_PCMCIA (Nb) + 3*PCMCIAPrtSp) +#define _PCMCIAAttr(Nb) /* PCMCIA Attribute [0..1] */ \ + (_PCMCIA (Nb) + 2*PCMCIAPrtSp) +#define _PCMCIAMem(Nb) /* PCMCIA Memory [0..1] */ \ + (_PCMCIA (Nb) + 3*PCMCIAPrtSp) #define _PCMCIA0 _PCMCIA (0) /* PCMCIA 0 */ #define _PCMCIA0IO _PCMCIAIO (0) /* PCMCIA 0 I/O */ @@ -1129,6 +1129,11 @@ #define ICPR __REG(0x40D00010) /* Interrupt Controller Pending Register */ #define ICCR __REG(0x40D00014) /* Interrupt Controller Control Register */ +#define ICIP2 __REG(0x40D0009C) /* Interrupt Controller IRQ Pending Register 2 */ +#define ICMR2 __REG(0x40D000A0) /* Interrupt Controller Mask Register 2 */ +#define ICLR2 __REG(0x40D000A4) /* Interrupt Controller Level Register 2 */ +#define ICFP2 __REG(0x40D000A8) /* Interrupt Controller FIQ Pending Register 2 */ +#define ICPR2 __REG(0x40D000AC) /* Interrupt Controller Pending Register 2 */ /* * General Purpose I/O @@ -1200,12 +1205,6 @@ /* Interrupt Controller */ -#define ICIP2 __REG(0x40D0009C) /* Interrupt Controller IRQ Pending Register 2 */ -#define ICMR2 __REG(0x40D000A0) /* Interrupt Controller Mask Register 2 */ -#define ICLR2 __REG(0x40D000A4) /* Interrupt Controller Level Register 2 */ -#define ICFP2 __REG(0x40D000A8) /* Interrupt Controller FIQ Pending Register 2 */ -#define ICPR2 __REG(0x40D000AC) /* Interrupt Controller Pending Register 2 */ - #define _GPLR(x) __REG2(0x40E00000, ((x) & 0x60) >> 3) #define _GPDR(x) __REG2(0x40E0000C, ((x) & 0x60) >> 3) #define _GPSR(x) __REG2(0x40E00018, ((x) & 0x60) >> 3) @@ -1237,267 +1236,6 @@ #endif - -/* GPIO alternate function assignments */ - -#define GPIO1_RST 1 /* reset */ -#define GPIO6_MMCCLK 6 /* MMC Clock */ -#define GPIO7_48MHz 7 /* 48 MHz clock output */ -#define GPIO8_MMCCS0 8 /* MMC Chip Select 0 */ -#define GPIO9_MMCCS1 9 /* MMC Chip Select 1 */ -#define GPIO10_RTCCLK 10 /* real time clock (1 Hz) */ -#define GPIO11_3_6MHz 11 /* 3.6 MHz oscillator out */ -#define GPIO12_32KHz 12 /* 32 kHz out */ -#define GPIO13_MBGNT 13 /* memory controller grant */ -#define GPIO14_MBREQ 14 /* alternate bus master request */ -#define GPIO15_nCS_1 15 /* chip select 1 */ -#define GPIO16_PWM0 16 /* PWM0 output */ -#define GPIO17_PWM1 17 /* PWM1 output */ -#define GPIO18_RDY 18 /* Ext. Bus Ready */ -#define GPIO19_DREQ1 19 /* External DMA Request */ -#define GPIO20_DREQ0 20 /* External DMA Request */ -#define GPIO23_SCLK 23 /* SSP clock */ -#define GPIO24_SFRM 24 /* SSP Frame */ -#define GPIO25_STXD 25 /* SSP transmit */ -#define GPIO26_SRXD 26 /* SSP receive */ -#define GPIO27_SEXTCLK 27 /* SSP ext_clk */ -#define GPIO28_BITCLK 28 /* AC97/I2S bit_clk */ -#define GPIO29_SDATA_IN 29 /* AC97 Sdata_in0 / I2S Sdata_in */ -#define GPIO30_SDATA_OUT 30 /* AC97/I2S Sdata_out */ -#define GPIO31_SYNC 31 /* AC97/I2S sync */ -#define GPIO32_SDATA_IN1 32 /* AC97 Sdata_in1 */ -#define GPIO32_SYSCLK 32 /* I2S System Clock */ -#define GPIO32_MMCCLK 32 /* MMC Clock (PXA270) */ -#define GPIO33_nCS_5 33 /* chip select 5 */ -#define GPIO34_FFRXD 34 /* FFUART receive */ -#define GPIO34_MMCCS0 34 /* MMC Chip Select 0 */ -#define GPIO35_FFCTS 35 /* FFUART Clear to send */ -#define GPIO36_FFDCD 36 /* FFUART Data carrier detect */ -#define GPIO37_FFDSR 37 /* FFUART data set ready */ -#define GPIO38_FFRI 38 /* FFUART Ring Indicator */ -#define GPIO39_MMCCS1 39 /* MMC Chip Select 1 */ -#define GPIO39_FFTXD 39 /* FFUART transmit data */ -#define GPIO40_FFDTR 40 /* FFUART data terminal Ready */ -#define GPIO41_FFRTS 41 /* FFUART request to send */ -#define GPIO42_BTRXD 42 /* BTUART receive data */ -#define GPIO42_HWRXD 42 /* HWUART receive data */ -#define GPIO43_BTTXD 43 /* BTUART transmit data */ -#define GPIO43_HWTXD 43 /* HWUART transmit data */ -#define GPIO44_BTCTS 44 /* BTUART clear to send */ -#define GPIO44_HWCTS 44 /* HWUART clear to send */ -#define GPIO45_BTRTS 45 /* BTUART request to send */ -#define GPIO45_HWRTS 45 /* HWUART request to send */ -#define GPIO45_AC97_SYSCLK 45 /* AC97 System Clock */ -#define GPIO46_ICPRXD 46 /* ICP receive data */ -#define GPIO46_STRXD 46 /* STD_UART receive data */ -#define GPIO47_ICPTXD 47 /* ICP transmit data */ -#define GPIO47_STTXD 47 /* STD_UART transmit data */ -#define GPIO48_nPOE 48 /* Output Enable for Card Space */ -#define GPIO49_nPWE 49 /* Write Enable for Card Space */ -#define GPIO50_nPIOR 50 /* I/O Read for Card Space */ -#define GPIO51_nPIOW 51 /* I/O Write for Card Space */ -#define GPIO52_nPCE_1 52 /* Card Enable for Card Space */ -#define GPIO53_nPCE_2 53 /* Card Enable for Card Space */ -#define GPIO53_MMCCLK 53 /* MMC Clock */ -#define GPIO54_MMCCLK 54 /* MMC Clock */ -#define GPIO54_pSKTSEL 54 /* Socket Select for Card Space */ -#define GPIO54_nPCE_2 54 /* Card Enable for Card Space (PXA27x) */ -#define GPIO55_nPREG 55 /* Card Address bit 26 */ -#define GPIO56_nPWAIT 56 /* Wait signal for Card Space */ -#define GPIO57_nIOIS16 57 /* Bus Width select for I/O Card Space */ -#define GPIO58_LDD_0 58 /* LCD data pin 0 */ -#define GPIO59_LDD_1 59 /* LCD data pin 1 */ -#define GPIO60_LDD_2 60 /* LCD data pin 2 */ -#define GPIO61_LDD_3 61 /* LCD data pin 3 */ -#define GPIO62_LDD_4 62 /* LCD data pin 4 */ -#define GPIO63_LDD_5 63 /* LCD data pin 5 */ -#define GPIO64_LDD_6 64 /* LCD data pin 6 */ -#define GPIO65_LDD_7 65 /* LCD data pin 7 */ -#define GPIO66_LDD_8 66 /* LCD data pin 8 */ -#define GPIO66_MBREQ 66 /* alternate bus master req */ -#define GPIO67_LDD_9 67 /* LCD data pin 9 */ -#define GPIO67_MMCCS0 67 /* MMC Chip Select 0 */ -#define GPIO68_LDD_10 68 /* LCD data pin 10 */ -#define GPIO68_MMCCS1 68 /* MMC Chip Select 1 */ -#define GPIO69_LDD_11 69 /* LCD data pin 11 */ -#define GPIO69_MMCCLK 69 /* MMC_CLK */ -#define GPIO70_LDD_12 70 /* LCD data pin 12 */ -#define GPIO70_RTCCLK 70 /* Real Time clock (1 Hz) */ -#define GPIO71_LDD_13 71 /* LCD data pin 13 */ -#define GPIO71_3_6MHz 71 /* 3.6 MHz Oscillator clock */ -#define GPIO72_LDD_14 72 /* LCD data pin 14 */ -#define GPIO72_32kHz 72 /* 32 kHz clock */ -#define GPIO73_LDD_15 73 /* LCD data pin 15 */ -#define GPIO73_MBGNT 73 /* Memory controller grant */ -#define GPIO74_LCD_FCLK 74 /* LCD Frame clock */ -#define GPIO75_LCD_LCLK 75 /* LCD line clock */ -#define GPIO76_LCD_PCLK 76 /* LCD Pixel clock */ -#define GPIO77_LCD_ACBIAS 77 /* LCD AC Bias */ -#define GPIO78_nCS_2 78 /* chip select 2 */ -#define GPIO79_nCS_3 79 /* chip select 3 */ -#define GPIO80_nCS_4 80 /* chip select 4 */ -#define GPIO81_NSCLK 81 /* NSSP clock */ -#define GPIO82_NSFRM 82 /* NSSP Frame */ -#define GPIO83_NSTXD 83 /* NSSP transmit */ -#define GPIO84_NSRXD 84 /* NSSP receive */ -#define GPIO85_nPCE_1 85 /* Card Enable for Card Space (PXA27x) */ -#define GPIO92_MMCDAT0 92 /* MMC DAT0 (PXA27x) */ -#define GPIO102_nPCE_1 102 /* PCMCIA (PXA27x) */ -#define GPIO109_MMCDAT1 109 /* MMC DAT1 (PXA27x) */ -#define GPIO110_MMCDAT2 110 /* MMC DAT2 (PXA27x) */ -#define GPIO110_MMCCS0 110 /* MMC Chip Select 0 (PXA27x) */ -#define GPIO111_MMCDAT3 111 /* MMC DAT3 (PXA27x) */ -#define GPIO111_MMCCS1 111 /* MMC Chip Select 1 (PXA27x) */ -#define GPIO112_MMCCMD 112 /* MMC CMD (PXA27x) */ -#define GPIO113_I2S_SYSCLK 113 /* I2S System Clock (PXA27x) */ -#define GPIO113_AC97_RESET_N 113 /* AC97 NRESET on (PXA27x) */ - -/* GPIO alternate function mode & direction */ - -#define GPIO_IN 0x000 -#define GPIO_OUT 0x080 -#define GPIO_ALT_FN_1_IN 0x100 -#define GPIO_ALT_FN_1_OUT 0x180 -#define GPIO_ALT_FN_2_IN 0x200 -#define GPIO_ALT_FN_2_OUT 0x280 -#define GPIO_ALT_FN_3_IN 0x300 -#define GPIO_ALT_FN_3_OUT 0x380 -#define GPIO_MD_MASK_NR 0x07f -#define GPIO_MD_MASK_DIR 0x080 -#define GPIO_MD_MASK_FN 0x300 -#define GPIO_DFLT_LOW 0x400 -#define GPIO_DFLT_HIGH 0x800 - -#define GPIO1_RTS_MD ( 1 | GPIO_ALT_FN_1_IN) -#define GPIO6_MMCCLK_MD ( 6 | GPIO_ALT_FN_1_OUT) -#define GPIO7_48MHz_MD ( 7 | GPIO_ALT_FN_1_OUT) -#define GPIO8_MMCCS0_MD ( 8 | GPIO_ALT_FN_1_OUT) -#define GPIO9_MMCCS1_MD ( 9 | GPIO_ALT_FN_1_OUT) -#define GPIO10_RTCCLK_MD (10 | GPIO_ALT_FN_1_OUT) -#define GPIO11_3_6MHz_MD (11 | GPIO_ALT_FN_1_OUT) -#define GPIO12_32KHz_MD (12 | GPIO_ALT_FN_1_OUT) -#define GPIO13_MBGNT_MD (13 | GPIO_ALT_FN_2_OUT) -#define GPIO14_MBREQ_MD (14 | GPIO_ALT_FN_1_IN) -#define GPIO15_nCS_1_MD (15 | GPIO_ALT_FN_2_OUT) -#define GPIO16_PWM0_MD (16 | GPIO_ALT_FN_2_OUT) -#define GPIO17_PWM1_MD (17 | GPIO_ALT_FN_2_OUT) -#define GPIO18_RDY_MD (18 | GPIO_ALT_FN_1_IN) -#define GPIO19_DREQ1_MD (19 | GPIO_ALT_FN_1_IN) -#define GPIO20_DREQ0_MD (20 | GPIO_ALT_FN_1_IN) -#define GPIO23_SCLK_MD (23 | GPIO_ALT_FN_2_OUT) -#define GPIO24_SFRM_MD (24 | GPIO_ALT_FN_2_OUT) -#define GPIO25_STXD_MD (25 | GPIO_ALT_FN_2_OUT) -#define GPIO26_SRXD_MD (26 | GPIO_ALT_FN_1_IN) -#define GPIO27_SEXTCLK_MD (27 | GPIO_ALT_FN_1_IN) -#define GPIO28_BITCLK_AC97_MD (28 | GPIO_ALT_FN_1_IN) -#define GPIO28_BITCLK_IN_I2S_MD (28 | GPIO_ALT_FN_2_IN) -#define GPIO28_BITCLK_OUT_I2S_MD (28 | GPIO_ALT_FN_1_OUT) -#define GPIO29_SDATA_IN_AC97_MD (29 | GPIO_ALT_FN_1_IN) -#define GPIO29_SDATA_IN_I2S_MD (29 | GPIO_ALT_FN_2_IN) -#define GPIO30_SDATA_OUT_AC97_MD (30 | GPIO_ALT_FN_2_OUT) -#define GPIO30_SDATA_OUT_I2S_MD (30 | GPIO_ALT_FN_1_OUT) -#define GPIO31_SYNC_I2S_MD (31 | GPIO_ALT_FN_1_OUT) -#define GPIO31_SYNC_AC97_MD (31 | GPIO_ALT_FN_2_OUT) -#define GPIO32_SDATA_IN1_AC97_MD (32 | GPIO_ALT_FN_1_IN) -#define GPIO32_SYSCLK_I2S_MD (32 | GPIO_ALT_FN_1_OUT) -#define GPIO32_MMCCLK_MD ( 32 | GPIO_ALT_FN_2_OUT) -#define GPIO33_nCS_5_MD (33 | GPIO_ALT_FN_2_OUT) -#define GPIO34_FFRXD_MD (34 | GPIO_ALT_FN_1_IN) -#define GPIO34_MMCCS0_MD (34 | GPIO_ALT_FN_2_OUT) -#define GPIO35_FFCTS_MD (35 | GPIO_ALT_FN_1_IN) -#define GPIO36_FFDCD_MD (36 | GPIO_ALT_FN_1_IN) -#define GPIO37_FFDSR_MD (37 | GPIO_ALT_FN_1_IN) -#define GPIO38_FFRI_MD (38 | GPIO_ALT_FN_1_IN) -#define GPIO39_MMCCS1_MD (39 | GPIO_ALT_FN_1_OUT) -#define GPIO39_FFTXD_MD (39 | GPIO_ALT_FN_2_OUT) -#define GPIO40_FFDTR_MD (40 | GPIO_ALT_FN_2_OUT) -#define GPIO41_FFRTS_MD (41 | GPIO_ALT_FN_2_OUT) -#define GPIO42_BTRXD_MD (42 | GPIO_ALT_FN_1_IN) -#define GPIO42_HWRXD_MD (42 | GPIO_ALT_FN_3_IN) -#define GPIO43_BTTXD_MD (43 | GPIO_ALT_FN_2_OUT) -#define GPIO43_HWTXD_MD (43 | GPIO_ALT_FN_3_OUT) -#define GPIO44_BTCTS_MD (44 | GPIO_ALT_FN_1_IN) -#define GPIO44_HWCTS_MD (44 | GPIO_ALT_FN_3_IN) -#define GPIO45_BTRTS_MD (45 | GPIO_ALT_FN_2_OUT) -#define GPIO45_HWRTS_MD (45 | GPIO_ALT_FN_3_OUT) -#define GPIO45_SYSCLK_AC97_MD (45 | GPIO_ALT_FN_1_OUT) -#define GPIO46_ICPRXD_MD (46 | GPIO_ALT_FN_1_IN) -#define GPIO46_STRXD_MD (46 | GPIO_ALT_FN_2_IN) -#define GPIO47_ICPTXD_MD (47 | GPIO_ALT_FN_2_OUT) -#define GPIO47_STTXD_MD (47 | GPIO_ALT_FN_1_OUT) -#define GPIO48_nPOE_MD (48 | GPIO_ALT_FN_2_OUT) -#define GPIO48_HWTXD_MD (48 | GPIO_ALT_FN_1_OUT) -#define GPIO48_nPOE_MD (48 | GPIO_ALT_FN_2_OUT) -#define GPIO49_HWRXD_MD (49 | GPIO_ALT_FN_1_IN) -#define GPIO49_nPWE_MD (49 | GPIO_ALT_FN_2_OUT) -#define GPIO50_nPIOR_MD (50 | GPIO_ALT_FN_2_OUT) -#define GPIO50_HWCTS_MD (50 | GPIO_ALT_FN_1_IN) -#define GPIO51_HWRTS_MD (51 | GPIO_ALT_FN_1_OUT) -#define GPIO51_nPIOW_MD (51 | GPIO_ALT_FN_2_OUT) -#define GPIO52_nPCE_1_MD (52 | GPIO_ALT_FN_2_OUT) -#define GPIO53_nPCE_2_MD (53 | GPIO_ALT_FN_2_OUT) -#define GPIO53_MMCCLK_MD (53 | GPIO_ALT_FN_1_OUT) -#define GPIO54_MMCCLK_MD (54 | GPIO_ALT_FN_1_OUT) -#define GPIO54_nPCE_2_MD (54 | GPIO_ALT_FN_2_OUT) -#define GPIO54_pSKTSEL_MD (54 | GPIO_ALT_FN_2_OUT) -#define GPIO55_nPREG_MD (55 | GPIO_ALT_FN_2_OUT) -#define GPIO56_nPWAIT_MD (56 | GPIO_ALT_FN_1_IN) -#define GPIO57_nIOIS16_MD (57 | GPIO_ALT_FN_1_IN) -#define GPIO58_LDD_0_MD (58 | GPIO_ALT_FN_2_OUT) -#define GPIO59_LDD_1_MD (59 | GPIO_ALT_FN_2_OUT) -#define GPIO60_LDD_2_MD (60 | GPIO_ALT_FN_2_OUT) -#define GPIO61_LDD_3_MD (61 | GPIO_ALT_FN_2_OUT) -#define GPIO62_LDD_4_MD (62 | GPIO_ALT_FN_2_OUT) -#define GPIO63_LDD_5_MD (63 | GPIO_ALT_FN_2_OUT) -#define GPIO64_LDD_6_MD (64 | GPIO_ALT_FN_2_OUT) -#define GPIO65_LDD_7_MD (65 | GPIO_ALT_FN_2_OUT) -#define GPIO66_LDD_8_MD (66 | GPIO_ALT_FN_2_OUT) -#define GPIO66_MBREQ_MD (66 | GPIO_ALT_FN_1_IN) -#define GPIO67_LDD_9_MD (67 | GPIO_ALT_FN_2_OUT) -#define GPIO67_MMCCS0_MD (67 | GPIO_ALT_FN_1_OUT) -#define GPIO68_LDD_10_MD (68 | GPIO_ALT_FN_2_OUT) -#define GPIO68_MMCCS1_MD (68 | GPIO_ALT_FN_1_OUT) -#define GPIO69_LDD_11_MD (69 | GPIO_ALT_FN_2_OUT) -#define GPIO69_MMCCLK_MD (69 | GPIO_ALT_FN_1_OUT) -#define GPIO70_LDD_12_MD (70 | GPIO_ALT_FN_2_OUT) -#define GPIO70_RTCCLK_MD (70 | GPIO_ALT_FN_1_OUT) -#define GPIO71_LDD_13_MD (71 | GPIO_ALT_FN_2_OUT) -#define GPIO71_3_6MHz_MD (71 | GPIO_ALT_FN_1_OUT) -#define GPIO72_LDD_14_MD (72 | GPIO_ALT_FN_2_OUT) -#define GPIO72_32kHz_MD (72 | GPIO_ALT_FN_1_OUT) -#define GPIO73_LDD_15_MD (73 | GPIO_ALT_FN_2_OUT) -#define GPIO73_MBGNT_MD (73 | GPIO_ALT_FN_1_OUT) -#define GPIO74_LCD_FCLK_MD (74 | GPIO_ALT_FN_2_OUT) -#define GPIO75_LCD_LCLK_MD (75 | GPIO_ALT_FN_2_OUT) -#define GPIO76_LCD_PCLK_MD (76 | GPIO_ALT_FN_2_OUT) -#define GPIO77_LCD_ACBIAS_MD (77 | GPIO_ALT_FN_2_OUT) -#define GPIO78_nCS_2_MD (78 | GPIO_ALT_FN_2_OUT) -#define GPIO79_nCS_3_MD (79 | GPIO_ALT_FN_2_OUT) -#define GPIO79_pSKTSEL_MD (79 | GPIO_ALT_FN_1_OUT) -#define GPIO80_nCS_4_MD (80 | GPIO_ALT_FN_2_OUT) -#define GPIO81_NSSP_CLK_OUT (81 | GPIO_ALT_FN_1_OUT) -#define GPIO81_NSSP_CLK_IN (81 | GPIO_ALT_FN_1_IN) -#define GPIO82_NSSP_FRM_OUT (82 | GPIO_ALT_FN_1_OUT) -#define GPIO82_NSSP_FRM_IN (82 | GPIO_ALT_FN_1_IN) -#define GPIO83_NSSP_TX (83 | GPIO_ALT_FN_1_OUT) -#define GPIO83_NSSP_RX (83 | GPIO_ALT_FN_2_IN) -#define GPIO84_NSSP_TX (84 | GPIO_ALT_FN_1_OUT) -#define GPIO84_NSSP_RX (84 | GPIO_ALT_FN_2_IN) -#define GPIO85_nPCE_1_MD (85 | GPIO_ALT_FN_1_OUT) -#define GPIO92_MMCDAT0_MD (92 | GPIO_ALT_FN_1_OUT) -#define GPIO102_nPCE_1_MD (102 | GPIO_ALT_FN_1_OUT) -#define GPIO104_pSKTSEL_MD (104 | GPIO_ALT_FN_1_OUT) -#define GPIO109_MMCDAT1_MD (109 | GPIO_ALT_FN_1_OUT) -#define GPIO110_MMCDAT2_MD (110 | GPIO_ALT_FN_1_OUT) -#define GPIO110_MMCCS0_MD (110 | GPIO_ALT_FN_1_OUT) -#define GPIO111_MMCDAT3_MD (111 | GPIO_ALT_FN_1_OUT) -#define GPIO110_MMCCS1_MD (111 | GPIO_ALT_FN_1_OUT) -#define GPIO112_MMCCMD_MD (112 | GPIO_ALT_FN_1_OUT) -#define GPIO113_I2S_SYSCLK_MD (113 | GPIO_ALT_FN_1_OUT) -#define GPIO113_AC97_RESET_N_MD (113 | GPIO_ALT_FN_2_OUT) -#define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_IN) -#define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN) - /* * Power Manager */ @@ -1668,260 +1406,8 @@ #define OSCC_OON (1 << 1) /* 32.768kHz OON (write-once only bit) */ #define OSCC_OOK (1 << 0) /* 32.768kHz OOK (read-only bit) */ - -/* - * LCD - */ - -#define LCCR0 __REG(0x44000000) /* LCD Controller Control Register 0 */ -#define LCCR1 __REG(0x44000004) /* LCD Controller Control Register 1 */ -#define LCCR2 __REG(0x44000008) /* LCD Controller Control Register 2 */ -#define LCCR3 __REG(0x4400000C) /* LCD Controller Control Register 3 */ -#define LCCR4 __REG(0x44000010) /* LCD Controller Control Register 3 */ -#define DFBR0 __REG(0x44000020) /* DMA Channel 0 Frame Branch Register */ -#define DFBR1 __REG(0x44000024) /* DMA Channel 1 Frame Branch Register */ -#define LCSR __REG(0x44000038) /* LCD Controller Status Register */ -#define LIIDR __REG(0x4400003C) /* LCD Controller Interrupt ID Register */ -#define TMEDRGBR __REG(0x44000040) /* TMED RGB Seed Register */ -#define TMEDCR __REG(0x44000044) /* TMED Control Register */ - -#define LCCR3_1BPP (0 << 24) -#define LCCR3_2BPP (1 << 24) -#define LCCR3_4BPP (2 << 24) -#define LCCR3_8BPP (3 << 24) -#define LCCR3_16BPP (4 << 24) - -#define LCCR3_PDFOR_0 (0 << 30) -#define LCCR3_PDFOR_1 (1 << 30) -#define LCCR3_PDFOR_2 (2 << 30) -#define LCCR3_PDFOR_3 (3 << 30) - -#define LCCR4_PAL_FOR_0 (0 << 15) -#define LCCR4_PAL_FOR_1 (1 << 15) -#define LCCR4_PAL_FOR_2 (2 << 15) -#define LCCR4_PAL_FOR_MASK (3 << 15) - -#define FDADR0 __REG(0x44000200) /* DMA Channel 0 Frame Descriptor Address Register */ -#define FSADR0 __REG(0x44000204) /* DMA Channel 0 Frame Source Address Register */ -#define FIDR0 __REG(0x44000208) /* DMA Channel 0 Frame ID Register */ -#define LDCMD0 __REG(0x4400020C) /* DMA Channel 0 Command Register */ -#define FDADR1 __REG(0x44000210) /* DMA Channel 1 Frame Descriptor Address Register */ -#define FSADR1 __REG(0x44000214) /* DMA Channel 1 Frame Source Address Register */ -#define FIDR1 __REG(0x44000218) /* DMA Channel 1 Frame ID Register */ -#define LDCMD1 __REG(0x4400021C) /* DMA Channel 1 Command Register */ - -#define LCCR0_ENB (1 << 0) /* LCD Controller enable */ -#define LCCR0_CMS (1 << 1) /* Color/Monochrome Display Select */ -#define LCCR0_Color (LCCR0_CMS*0) /* Color display */ -#define LCCR0_Mono (LCCR0_CMS*1) /* Monochrome display */ -#define LCCR0_SDS (1 << 2) /* Single/Dual Panel Display */ - /* Select */ -#define LCCR0_Sngl (LCCR0_SDS*0) /* Single panel display */ -#define LCCR0_Dual (LCCR0_SDS*1) /* Dual panel display */ - -#define LCCR0_LDM (1 << 3) /* LCD Disable Done Mask */ -#define LCCR0_SFM (1 << 4) /* Start of frame mask */ -#define LCCR0_IUM (1 << 5) /* Input FIFO underrun mask */ -#define LCCR0_EFM (1 << 6) /* End of Frame mask */ -#define LCCR0_PAS (1 << 7) /* Passive/Active display Select */ -#define LCCR0_Pas (LCCR0_PAS*0) /* Passive display (STN) */ -#define LCCR0_Act (LCCR0_PAS*1) /* Active display (TFT) */ -#define LCCR0_DPD (1 << 9) /* Double Pixel Data (monochrome */ - /* display mode) */ -#define LCCR0_4PixMono (LCCR0_DPD*0) /* 4-Pixel/clock Monochrome */ - /* display */ -#define LCCR0_8PixMono (LCCR0_DPD*1) /* 8-Pixel/clock Monochrome */ - /* display */ -#define LCCR0_DIS (1 << 10) /* LCD Disable */ -#define LCCR0_QDM (1 << 11) /* LCD Quick Disable mask */ -#define LCCR0_PDD (0xff << 12) /* Palette DMA request delay */ -#define LCCR0_PDD_S 12 -#define LCCR0_BM (1 << 20) /* Branch mask */ -#define LCCR0_OUM (1 << 21) /* Output FIFO underrun mask */ -#define LCCR0_LCDT (1 << 22) /* LCD panel type */ -#define LCCR0_RDSTM (1 << 23) /* Read status interrupt mask */ -#define LCCR0_CMDIM (1 << 24) /* Command interrupt mask */ -#define LCCR0_OUC (1 << 25) /* Overlay Underlay control bit */ -#define LCCR0_LDDALT (1 << 26) /* LDD alternate mapping control */ - -#define LCCR1_PPL Fld (10, 0) /* Pixels Per Line - 1 */ -#define LCCR1_DisWdth(Pixel) /* Display Width [1..800 pix.] */ \ - (((Pixel) - 1) << FShft (LCCR1_PPL)) - -#define LCCR1_HSW Fld (6, 10) /* Horizontal Synchronization */ -#define LCCR1_HorSnchWdth(Tpix) /* Horizontal Synchronization */ \ - /* pulse Width [1..64 Tpix] */ \ - (((Tpix) - 1) << FShft (LCCR1_HSW)) - -#define LCCR1_ELW Fld (8, 16) /* End-of-Line pixel clock Wait */ - /* count - 1 [Tpix] */ -#define LCCR1_EndLnDel(Tpix) /* End-of-Line Delay */ \ - /* [1..256 Tpix] */ \ - (((Tpix) - 1) << FShft (LCCR1_ELW)) - -#define LCCR1_BLW Fld (8, 24) /* Beginning-of-Line pixel clock */ - /* Wait count - 1 [Tpix] */ -#define LCCR1_BegLnDel(Tpix) /* Beginning-of-Line Delay */ \ - /* [1..256 Tpix] */ \ - (((Tpix) - 1) << FShft (LCCR1_BLW)) - - -#define LCCR2_LPP Fld (10, 0) /* Line Per Panel - 1 */ -#define LCCR2_DisHght(Line) /* Display Height [1..1024 lines] */ \ - (((Line) - 1) << FShft (LCCR2_LPP)) - -#define LCCR2_VSW Fld (6, 10) /* Vertical Synchronization pulse */ - /* Width - 1 [Tln] (L_FCLK) */ -#define LCCR2_VrtSnchWdth(Tln) /* Vertical Synchronization pulse */ \ - /* Width [1..64 Tln] */ \ - (((Tln) - 1) << FShft (LCCR2_VSW)) - -#define LCCR2_EFW Fld (8, 16) /* End-of-Frame line clock Wait */ - /* count [Tln] */ -#define LCCR2_EndFrmDel(Tln) /* End-of-Frame Delay */ \ - /* [0..255 Tln] */ \ - ((Tln) << FShft (LCCR2_EFW)) - -#define LCCR2_BFW Fld (8, 24) /* Beginning-of-Frame line clock */ - /* Wait count [Tln] */ -#define LCCR2_BegFrmDel(Tln) /* Beginning-of-Frame Delay */ \ - /* [0..255 Tln] */ \ - ((Tln) << FShft (LCCR2_BFW)) - -#if 0 /* keep */ -#define LCCR3_PCD (0xff) /* Pixel clock divisor */ -#define LCCR3_ACB (0xff << 8) /* AC Bias pin frequency */ -#define LCCR3_ACB_S 8 -#endif - -#define LCCR3_API (0xf << 16) /* AC Bias pin trasitions per interrupt */ -#define LCCR3_API_S 16 -#define LCCR3_VSP (1 << 20) /* vertical sync polarity */ -#define LCCR3_HSP (1 << 21) /* horizontal sync polarity */ -#define LCCR3_PCP (1 << 22) /* Pixel Clock Polarity (L_PCLK) */ -#define LCCR3_PixRsEdg (LCCR3_PCP*0) /* Pixel clock Rising-Edge */ -#define LCCR3_PixFlEdg (LCCR3_PCP*1) /* Pixel clock Falling-Edge */ - -#define LCCR3_OEP (1 << 23) /* Output Enable Polarity (L_BIAS, */ - /* active display mode) */ -#define LCCR3_OutEnH (LCCR3_OEP*0) /* Output Enable active High */ -#define LCCR3_OutEnL (LCCR3_OEP*1) /* Output Enable active Low */ - -#if 0 /* keep */ -#define LCCR3_BPP (7 << 24) /* bits per pixel */ -#define LCCR3_BPP_S 24 -#endif -#define LCCR3_DPC (1 << 27) /* double pixel clock mode */ - - -#define LCCR3_PCD Fld (8, 0) /* Pixel Clock Divisor */ -#define LCCR3_PixClkDiv(Div) /* Pixel Clock Divisor */ \ - (((Div) << FShft (LCCR3_PCD))) - - -#define LCCR3_BPP Fld (3, 24) /* Bit Per Pixel */ -#define LCCR3_Bpp(Bpp) /* Bit Per Pixel */ \ - (((Bpp) << FShft (LCCR3_BPP))) - -#define LCCR3_ACB Fld (8, 8) /* AC Bias */ -#define LCCR3_Acb(Acb) /* BAC Bias */ \ - (((Acb) << FShft (LCCR3_ACB))) - -#define LCCR3_HorSnchH (LCCR3_HSP*0) /* Horizontal Synchronization */ - /* pulse active High */ -#define LCCR3_HorSnchL (LCCR3_HSP*1) /* Horizontal Synchronization */ - -#define LCCR3_VrtSnchH (LCCR3_VSP*0) /* Vertical Synchronization pulse */ - /* active High */ -#define LCCR3_VrtSnchL (LCCR3_VSP*1) /* Vertical Synchronization pulse */ - /* active Low */ - -#define LCSR_LDD (1 << 0) /* LCD Disable Done */ -#define LCSR_SOF (1 << 1) /* Start of frame */ -#define LCSR_BER (1 << 2) /* Bus error */ -#define LCSR_ABC (1 << 3) /* AC Bias count */ -#define LCSR_IUL (1 << 4) /* input FIFO underrun Lower panel */ -#define LCSR_IUU (1 << 5) /* input FIFO underrun Upper panel */ -#define LCSR_OU (1 << 6) /* output FIFO underrun */ -#define LCSR_QD (1 << 7) /* quick disable */ -#define LCSR_EOF (1 << 8) /* end of frame */ -#define LCSR_BS (1 << 9) /* branch status */ -#define LCSR_SINT (1 << 10) /* subsequent interrupt */ - -#define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */ - -#define LCSR_LDD (1 << 0) /* LCD Disable Done */ -#define LCSR_SOF (1 << 1) /* Start of frame */ -#define LCSR_BER (1 << 2) /* Bus error */ -#define LCSR_ABC (1 << 3) /* AC Bias count */ -#define LCSR_IUL (1 << 4) /* input FIFO underrun Lower panel */ -#define LCSR_IUU (1 << 5) /* input FIFO underrun Upper panel */ -#define LCSR_OU (1 << 6) /* output FIFO underrun */ -#define LCSR_QD (1 << 7) /* quick disable */ -#define LCSR_EOF (1 << 8) /* end of frame */ -#define LCSR_BS (1 << 9) /* branch status */ -#define LCSR_SINT (1 << 10) /* subsequent interrupt */ - -#define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */ - #ifdef CONFIG_PXA27x -/* - * Keypad - */ -#define KPC __REG(0x41500000) /* Keypad Interface Control register */ -#define KPDK __REG(0x41500008) /* Keypad Interface Direct Key register */ -#define KPREC __REG(0x41500010) /* Keypad Interface Rotary Encoder register */ -#define KPMK __REG(0x41500018) /* Keypad Interface Matrix Key register */ -#define KPAS __REG(0x41500020) /* Keypad Interface Automatic Scan register */ -#define KPASMKP0 __REG(0x41500028) /* Keypad Interface Automatic Scan Multiple Key Presser register 0 */ -#define KPASMKP1 __REG(0x41500030) /* Keypad Interface Automatic Scan Multiple Key Presser register 1 */ -#define KPASMKP2 __REG(0x41500038) /* Keypad Interface Automatic Scan Multiple Key Presser register 2 */ -#define KPASMKP3 __REG(0x41500040) /* Keypad Interface Automatic Scan Multiple Key Presser register 3 */ -#define KPKDI __REG(0x41500048) /* Keypad Interface Key Debounce Interval register */ - -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ -#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ -#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ -#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ -#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ -#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ -#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ -#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ -#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ -#define KPC_MS_ALL (KPC_MS0 | KPC_MS1 | KPC_MS2 | KPC_MS3 | KPC_MS4 | KPC_MS5 | KPC_MS6 | KPC_MS7) -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ -#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ -#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ -#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ - -#define KPDK_DKP (0x1 << 31) -#define KPDK_DK7 (0x1 << 7) -#define KPDK_DK6 (0x1 << 6) -#define KPDK_DK5 (0x1 << 5) -#define KPDK_DK4 (0x1 << 4) -#define KPDK_DK3 (0x1 << 3) -#define KPDK_DK2 (0x1 << 2) -#define KPDK_DK1 (0x1 << 1) -#define KPDK_DK0 (0x1 << 0) - -#define KPREC_OF1 (0x1 << 31) -#define kPREC_UF1 (0x1 << 30) -#define KPREC_OF0 (0x1 << 15) -#define KPREC_UF0 (0x1 << 14) - -#define KPMK_MKP (0x1 << 31) -#define KPAS_SO (0x1 << 31) -#define KPASMKPx_SO (0x1 << 31) - /* Camera Interface */ #define CICR0 __REG(0x50000000) #define CICR1 __REG(0x50000004) @@ -1953,7 +1439,7 @@ #define CICR0_FOM (1 << 0) /* FIFO-overrun mask */ #define CICR1_TBIT (1 << 31) /* Transparency bit */ -#define CICR1_RGBT_CONV (0x3 << 30) /* RGBT conversion mask */ +#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */ #define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */ #define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */ #define CICR1_RGB_F (1 << 11) /* RGB format */ diff --git a/linux/include/linux/i2c-id.h b/linux/include/linux/i2c-id.h index b979112f7..580acc939 100644 --- a/linux/include/linux/i2c-id.h +++ b/linux/include/linux/i2c-id.h @@ -79,12 +79,9 @@ #define I2C_DRIVERID_UPD64031A 79 /* upd64031a video processor */ #define I2C_DRIVERID_SAA717X 80 /* saa717x video encoder */ #define I2C_DRIVERID_DS1672 81 /* Dallas/Maxim DS1672 RTC */ -#define I2C_DRIVERID_X1205 82 /* Xicor/Intersil X1205 RTC */ -#define I2C_DRIVERID_PCF8563 83 /* Philips PCF8563 RTC */ #define I2C_DRIVERID_BT866 85 /* Conexant bt866 video encoder */ #define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */ #define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */ -#define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */ #define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */ #define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */ #define I2C_DRIVERID_WM8753 91 /* Wolfson WM8753 audio codec */ @@ -125,6 +122,7 @@ #define I2C_HW_B_CX2341X 0x010020 /* Conexant CX2341X MPEG encoder cards */ #define I2C_HW_B_INTELFB 0x010021 /* intel framebuffer driver */ #define I2C_HW_B_CX23885 0x010022 /* conexant 23885 based tv cards (bus1) */ +#define I2C_HW_B_AU0828 0x010023 /* auvitek au0828 usb bridge */ /* --- PCF 8584 based algorithms */ #define I2C_HW_P_ELEK 0x020002 /* Elektor ISA Bus inteface card */ diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 1760c4205..5359c2c76 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -246,6 +246,7 @@ struct v4l2_capability #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */ #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */ +#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */ #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ @@ -1156,6 +1157,14 @@ struct v4l2_frequency __u32 reserved[8]; }; +struct v4l2_hw_freq_seek { + __u32 tuner; + enum v4l2_tuner_type type; + __u32 seek_upward; + __u32 wrap_around; + __u32 reserved[8]; +}; + /* * A U D I O */ @@ -1441,6 +1450,7 @@ struct v4l2_chip_ident { #define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) #endif +#define VIDIOC_S_HW_FREQ_SEEK _IOW ('V', 82, struct v4l2_hw_freq_seek) #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ diff --git a/linux/include/media/ir-common.h b/linux/include/media/ir-common.h index 75a348286..bfee8be5d 100644 --- a/linux/include/media/ir-common.h +++ b/linux/include/media/ir-common.h @@ -142,6 +142,7 @@ extern IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_pinnacle_pctv_hd[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_genius_tvgo_a11mce[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE]; diff --git a/linux/include/media/v4l2-chip-ident.h b/linux/include/media/v4l2-chip-ident.h index 0ea0bd85c..2a5277427 100644 --- a/linux/include/media/v4l2-chip-ident.h +++ b/linux/include/media/v4l2-chip-ident.h @@ -64,6 +64,7 @@ enum { /* Conexant MPEG encoder/decoders: reserved range 410-420 */ V4L2_IDENT_CX23415 = 415, V4L2_IDENT_CX23416 = 416, + V4L2_IDENT_CX23418 = 418, /* module vp27smpx: just ident 2700 */ V4L2_IDENT_VP27SMPX = 2700, diff --git a/linux/include/media/v4l2-common.h b/linux/include/media/v4l2-common.h index 72ed0ddb2..47cc48ea9 100644 --- a/linux/include/media/v4l2-common.h +++ b/linux/include/media/v4l2-common.h @@ -113,9 +113,11 @@ int v4l2_chip_match_host(u32 id_type, u32 chip_id); struct i2c_driver; struct i2c_adapter; struct i2c_client; +struct i2c_device_id; int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver, - const char *name, int (*probe)(struct i2c_client *)); + const char *name, + int (*probe)(struct i2c_client *, const struct i2c_device_id *)); /* ------------------------------------------------------------------------- */ diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h index fac381990..2b4577abb 100644 --- a/linux/include/media/v4l2-dev.h +++ b/linux/include/media/v4l2-dev.h @@ -46,7 +46,6 @@ #define VFL_TYPE_VTX 3 /* Video standard functions */ -extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs); extern char *v4l2_norm_to_name(v4l2_std_id id); extern int v4l2_video_std_construct(struct v4l2_standard *vs, int id, char *name); @@ -323,6 +322,8 @@ struct video_device /* Log status ioctl */ int (*vidioc_log_status) (struct file *file, void *fh); + int (*vidioc_s_hw_freq_seek) (struct file *file, void *fh, + struct v4l2_hw_freq_seek *a); /* Debugging ioctls */ #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -334,6 +335,10 @@ struct video_device int (*vidioc_g_chip_ident) (struct file *file, void *fh, struct v4l2_chip_ident *chip); + /* For other private ioctls */ + int (*vidioc_default) (struct file *file, void *fh, + int cmd, void *arg); + #if 0 /* old, obsolete interface */ int (*open)(struct video_device *, int mode); void (*close)(struct video_device *); diff --git a/linux/include/media/v4l2-i2c-drv-legacy.h b/linux/include/media/v4l2-i2c-drv-legacy.h index 10c3231f0..7494bdcc3 100644 --- a/linux/include/media/v4l2-i2c-drv-legacy.h +++ b/linux/include/media/v4l2-i2c-drv-legacy.h @@ -25,7 +25,7 @@ struct v4l2_i2c_driver_data { const char * const name; int driverid; int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); - int (*probe)(struct i2c_client *client); + int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); int (*remove)(struct i2c_client *client); int (*suspend)(struct i2c_client *client, pm_message_t state); int (*resume)(struct i2c_client *client); @@ -168,6 +168,14 @@ static struct i2c_driver v4l2_i2c_driver = { }; #endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) +static int compat_legacy_probe(struct i2c_client *client) +{ + return v4l2_i2c_data.probe(client, NULL); +} +#endif + static int __init v4l2_i2c_drv_init(void) { int err; @@ -198,7 +206,11 @@ static int __init v4l2_i2c_drv_init(void) v4l2_i2c_driver.driver.name = v4l2_i2c_data.name; v4l2_i2c_driver.id = v4l2_i2c_data.driverid; v4l2_i2c_driver.command = v4l2_i2c_data.command; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) v4l2_i2c_driver.probe = v4l2_i2c_data.probe; +#else + v4l2_i2c_driver.probe = compat_legacy_probe; +#endif v4l2_i2c_driver.remove = v4l2_i2c_data.remove; err = i2c_add_driver(&v4l2_i2c_driver); if (err) diff --git a/linux/include/media/v4l2-i2c-drv.h b/linux/include/media/v4l2-i2c-drv.h index 8b5d74912..5b4959620 100644 --- a/linux/include/media/v4l2-i2c-drv.h +++ b/linux/include/media/v4l2-i2c-drv.h @@ -30,7 +30,7 @@ struct v4l2_i2c_driver_data { const char * const name; int driverid; int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); - int (*probe)(struct i2c_client *client); + int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); int (*remove)(struct i2c_client *client); int (*suspend)(struct i2c_client *client, pm_message_t state); int (*resume)(struct i2c_client *client); @@ -45,6 +45,13 @@ struct v4l2_i2c_driver_data { static struct v4l2_i2c_driver_data v4l2_i2c_data; static struct i2c_driver v4l2_i2c_driver; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) +static int compat_legacy_probe(struct i2c_client *client) +{ + return v4l2_i2c_data.probe(client, NULL); +} +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) /* Bus-based I2C implementation for kernels >= 2.6.22 */ @@ -54,7 +61,11 @@ static int __init v4l2_i2c_drv_init(void) v4l2_i2c_driver.driver.name = v4l2_i2c_data.name; v4l2_i2c_driver.id = v4l2_i2c_data.driverid; v4l2_i2c_driver.command = v4l2_i2c_data.command; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) v4l2_i2c_driver.probe = v4l2_i2c_data.probe; +#else + v4l2_i2c_driver.probe = compat_legacy_probe; +#endif v4l2_i2c_driver.remove = v4l2_i2c_data.remove; v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend; v4l2_i2c_driver.resume = v4l2_i2c_data.resume; diff --git a/linux/include/media/videobuf-core.h b/linux/include/media/videobuf-core.h index dd0821f40..a2f55ff24 100644 --- a/linux/include/media/videobuf-core.h +++ b/linux/include/media/videobuf-core.h @@ -241,6 +241,9 @@ unsigned int videobuf_poll_stream(struct file *file, int videobuf_mmap_setup(struct videobuf_queue *q, unsigned int bcount, unsigned int bsize, enum v4l2_memory memory); +int __videobuf_mmap_setup(struct videobuf_queue *q, + unsigned int bcount, unsigned int bsize, + enum v4l2_memory memory); int videobuf_mmap_free(struct videobuf_queue *q); int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma); diff --git a/mailimport b/mailimport index 7b233932e..76364aeea 100755 --- a/mailimport +++ b/mailimport @@ -112,6 +112,11 @@ apply_patch () { make whitespace hg qrefresh else + echo "WILL commit with this comment:" + cat $TMP2| grep -v "^#" + echo "Press <break> within 3 seconds if this is wrong." + sleep 3 + patch -s -t -p1 -l -N -d $pdir -i $next if [ "$?" != "0" ]; then echo "*** ERROR at: patch -s -t -p1 -l -N -d $pdir -i $next" diff --git a/v4l/compat.h b/v4l/compat.h index ea7d49acf..04950ea74 100644 --- a/v4l/compat.h +++ b/v4l/compat.h @@ -533,6 +533,8 @@ do { \ le16_to_cpu(get_unaligned((unsigned short *)(a))) #define put_unaligned_le16(r, a) \ put_unaligned(cpu_to_le16(r), ((unsigned short *)(a))) +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) #ifdef CONFIG_PROC_FS static inline struct proc_dir_entry *proc_create(const char *a, mode_t b, struct proc_dir_entry *c, const struct file_operations *d) @@ -547,7 +549,33 @@ static inline struct proc_dir_entry *proc_create(const char *a, } return e; } + +static inline struct proc_dir_entry *proc_create_data(const char *a, + mode_t b, struct proc_dir_entry *c, const struct file_operations *d, + void *f) +{ + struct proc_dir_entry *e; + + e = create_proc_entry(a, b, c); + if (e) { + e->owner = THIS_MODULE; + e->proc_fops = d; + e->data = f; + } + return e; +} +#endif #endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) +#define div64_u64(a,b) div64_64(a,b) + +#define clamp( x, l, h ) max_t( __typeof__( x ), \ + ( l ), \ + min_t( __typeof__( x ), \ + ( h ), \ + ( x ) ) ) + #endif #endif diff --git a/v4l/obsolete.txt b/v4l/obsolete.txt index 384224df8..e720a48b8 100644 --- a/v4l/obsolete.txt +++ b/v4l/obsolete.txt @@ -3,3 +3,21 @@ video/video-buf # This file were replaced by videobuf-dvb video/video-buf-dvb + +# Those drivers were moved to common/tuners +dvb/frontends/mt2060 +dvb/frontends/mt2131 +dvb/frontends/mt2266 +dvb/frontends/qt1010 +dvb/frontends/tda18271 +dvb/frontends/tda827x +dvb/frontends/xc5000 +video/mt20xx +video/tda8290 +video/tda9887 +video/tea5761 +video/tea5767 +video/tuner-simple +video/tuner-types +video/tuner-xc2028 + diff --git a/v4l/scripts/au0828.pl b/v4l/scripts/au0828.pl new file mode 100755 index 000000000..822a54afb --- /dev/null +++ b/v4l/scripts/au0828.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl -w +use strict; + +my $new_entry = -1; +my $nr = 0; +my ($id,$subvendor,$subdevice); +my %data; + +while (<>) { + # defines in header file + if (/#define\s+(AU08[\d]._BOARD_\w+)\s+(\d+)/) { + $data{$1}->{nr} = $2; + next; + } + # au0828_boards + if (/\[(AU0828_BOARD_\w+)\]/) { + $id = $1; + $data{$id}->{id} = $id; + $data{$id}->{type} = "(au0828)"; +# $data{$id}->{nr} = $nr++; + }; + + next unless defined($id); + + if (/USB_DEVICE.*0x([0-9a-fA-F]*).*0x([0-9a-fA-F]*)/ ) { + $subvendor=$1; + $subdevice=$2; + } + + if (/.*driver_info.*(AU08[\d]._BOARD_\w+)/ ) { + push @{$data{$1}->{subid}}, "$subvendor:$subdevice"; + } + + if (!defined($data{$id}) || !defined($data{$id}->{name})) { + $data{$id}->{name} = $1 if (/\.name\s*=\s*\"([^\"]+)\"/); + } + + # au0828_USB_tbl + + +} + +foreach my $item (sort { $data{$a}->{nr} <=> $data{$b}->{nr} } keys %data) { + printf("%3d -> %-40s %-15s", $data{$item}->{nr}, $data{$item}->{name},$data{$item}->{type}); + printf(" [%s]",join(",",@{$data{$item}->{subid}})) + if defined($data{$item}->{subid}); + print "\n"; +} diff --git a/v4l/scripts/cardlist b/v4l/scripts/cardlist index 3e5213975..7c903539c 100755 --- a/v4l/scripts/cardlist +++ b/v4l/scripts/cardlist @@ -6,10 +6,10 @@ scripts/bttv.pl ../linux/drivers/media/video/bt8xx/bttv.h ../linux/drivers/medi scripts/cx88.pl ../linux/drivers/media/video/cx88/cx88.h ../linux/drivers/media/video/cx88/cx88-cards.c \ | perl -ne 's/[ \t]+$//; print' > ../linux/Documentation/video4linux/CARDLIST.cx88 -scripts/em28xx.pl ../linux/drivers/media/video/em28xx/em28xx-cards.c \ +scripts/em28xx.pl ../linux/drivers/media/video/em28xx/em28xx-cards.c ../linux/drivers/media/video/em28xx/em28xx.h \ | perl -ne 's/[ \t]+$//; print' > ../linux/Documentation/video4linux/CARDLIST.em28xx -scripts/tuner.pl ../linux/include/media/tuner.h ../linux/drivers/media/video/tuner-types.c \ +scripts/tuner.pl ../linux/include/media/tuner.h ../linux/drivers/media/common/tuners/tuner-types.c \ | perl -ne 's/[ \t]+$//; print' > ../linux/Documentation/video4linux/CARDLIST.tuner scripts/saa7134.pl ../linux/drivers/media/video/saa7134/saa7134.h ../linux/drivers/media/video/saa7134/saa7134-cards.c \ @@ -20,3 +20,6 @@ scripts/usbvision.pl ../linux/drivers/media/video/usbvision/usbvision-cards.h .. scripts/cx23885.pl ../linux/drivers/media/video/cx23885/cx23885.h ../linux/drivers/media/video/cx23885/cx23885-cards.c \ | perl -ne 's/[ \t]+$//; print' > ../linux/Documentation/video4linux/CARDLIST.cx23885 + +scripts/au0828.pl ../linux/drivers/media/video/au0828/au0828-cards.h ../linux/drivers/media/video/au0828/au0828-cards.c \ + | perl -ne 's/[ \t]+$//; print' > ../linux/Documentation/video4linux/CARDLIST.au0828 diff --git a/v4l/scripts/hghead.pl b/v4l/scripts/hghead.pl index 9a6389311..1eb7355c4 100755 --- a/v4l/scripts/hghead.pl +++ b/v4l/scripts/hghead.pl @@ -25,7 +25,7 @@ my $fromname=""; open IN, "<$in"; while ($line = <IN>) { - if ($line =~ m/Index.*/) { + if ($line =~ m/^\s*Index.*/) { last; } if ($line =~ m/^diff .*/) { diff --git a/v4l/versions.txt b/v4l/versions.txt index 221f4f547..54be82237 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -126,7 +126,9 @@ VIDEO_SAA7127 VIDEO_UPD64031A VIDEO_UPD64083 VIDEO_IVTV +VIDEO_CX18 VIDEO_EM28XX +VIDEO_AU0828 DVB DVB_CORE DVB_AV7110 @@ -201,6 +203,9 @@ DVB_TUNER_MT2060 DVB_TUNER_MT2131 DVB_TDA827X DVB_TDA18271 +DVB_AU8522 +DVB_TDA10048 +DVB_S5H1411 VIDEO_SAA7146 VIDEO_SAA7146_VV VIDEO_TUNER diff --git a/v4l2-apps/util/Makefile b/v4l2-apps/util/Makefile index 34fc9a103..cc0547c32 100644 --- a/v4l2-apps/util/Makefile +++ b/v4l2-apps/util/Makefile @@ -1,8 +1,13 @@ # Makefile for linuxtv.org v4l2-apps/util -CPPFLAGS += -I../../linux/include +CPPFLAGS += -I../../linux/include -D_GNU_SOURCE +LDFLAGS += -lm -binaries = v4l2-ctl v4l2-dbg +binaries = v4l2-ctl v4l2-dbg ivtv-ctl cx18-ctl + +ifeq ($(prefix),) +prefix = /usr +endif .PHONY: all clean install qv4l2 @@ -22,6 +27,8 @@ v4l2-dbg: v4l2-dbg.o v4l2-driverids.o v4l2-chipids.o $(CXX) $^ -o $@ install: + mkdir -p $(prefix)/bin + cp $(binaries) $(prefix)/bin include ../Make.rules diff --git a/v4l2-apps/util/cx18-ctl.c b/v4l2-apps/util/cx18-ctl.c new file mode 100644 index 000000000..755b9437f --- /dev/null +++ b/v4l2-apps/util/cx18-ctl.c @@ -0,0 +1,496 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo dot com> + + Cleanup and VBI and audio in/out options: + Copyright (C) 2004 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <math.h> +#include <linux/types.h> +#include <sys/klog.h> + +#include <linux/videodev2.h> + +/* copied from cx18-driver.h */ +#define CX18_DBGFLG_WARN (1 << 0) +#define CX18_DBGFLG_INFO (1 << 1) +#define CX18_DBGFLG_API (1 << 2) +#define CX18_DBGFLG_DMA (1 << 3) +#define CX18_DBGFLG_IOCTL (1 << 4) +#define CX18_DBGFLG_FILE (1 << 5) +#define CX18_DBGFLG_I2C (1 << 6) +#define CX18_DBGFLG_IRQ (1 << 7) +/* Flag to turn on high volume debugging */ +#define CX18_DBGFLG_HIGHVOL (1 << 8) + +/* Internals copied from media/v4l2-common.h */ +struct v4l2_routing { + __u32 input; + __u32 output; +}; +#define VIDIOC_INT_S_AUDIO_ROUTING _IOW('d', 109, struct v4l2_routing) + +#define VIDIOC_INT_RESET _IOW('d', 102, __u32) + +#define __stringify_1(x) #x +#define __stringify(x) __stringify_1(x) + +#include "../../linux/drivers/media/video/cx18/cx18-version.h" + +/* GPIO */ +#define CX18_REG_GPIO_IN 0x02c72010 +#define CX18_REG_GPIO_OUT1 0x02c78100 +#define CX18_REG_GPIO_DIR1 0x02c78108 +#define CX18_REG_GPIO_OUT2 0x02c78104 +#define CX18_REG_GPIO_DIR2 0x02c7810c + +/* Options */ +enum Option { + OptSetDebugLevel = 'D', + OptSetDevice = 'd', + OptGetDebugLevel = 'e', + OptHelp = 'h', + OptSetGPIO = 'i', + OptListGPIO = 'I', + OptSetAudioRoute = 'v', + OptReset = 128, + OptVersion, + OptLast = 256 +}; + +static char options[OptLast]; + +static struct option long_options[] = { + /* Please keep in alphabetical order of the short option. + That makes it easier to see which options are still free. */ + {"set-debug", required_argument, 0, OptSetDebugLevel}, + {"device", required_argument, 0, OptSetDevice}, + {"get-debug", no_argument, 0, OptGetDebugLevel}, + {"help", no_argument, 0, OptHelp}, + {"set-gpio", required_argument, 0, OptSetGPIO}, + {"list-gpio", no_argument, 0, OptListGPIO}, + {"audio-route", required_argument, 0, OptSetAudioRoute}, + {"reset", required_argument, 0, OptReset}, + {"version", no_argument, 0, OptVersion}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf(" -d, --device <dev> use device <dev> instead of /dev/video0\n"); + printf(" -h, --help display this help message\n"); + printf(" --reset <mask> reset the infrared receiver (1) or digitizer (2) [VIDIOC_INT_RESET]\n"); + printf(" --version shows the version number of this utility.\n"); + printf(" It should match the driver version.\n"); + printf("\n"); + printf("Expert options:\n"); + printf(" -D, --set-debug <level>\n"); + printf(" set the module debug variable\n"); + printf(" 1/0x0001: warning\n"); + printf(" 2/0x0002: info\n"); + printf(" 4/0x0004: mailbox\n"); + printf(" 8/0x0008: dma\n"); + printf(" 16/0x0010: ioctl\n"); + printf(" 32/0x0020: file\n"); + printf(" 64/0x0040: i2c\n"); + printf(" 128/0x0080: irq\n"); + printf(" 256/0x0100: high volume\n"); + printf(" -e, --get-debug query the module debug variable\n"); + printf(" -I, --list-gpio\n"); + printf(" show GPIO input/direction/output bits\n"); + printf(" -i, --set-gpio [dir=<dir>,]val=<val>\n"); + printf(" set GPIO direction bits to <dir> and set output to <val>\n"); + printf(" -v, --audio-route=input=<in>,output=<out>\n"); + printf(" set the audio input/output routing [VIDIOC_INT_S_AUDIO_ROUTING]\n"); + exit(0); +} + +static char *pts_to_string(char *str, unsigned long pts, float fps) +{ + static char buf[256]; + int hours, minutes, seconds, fracsec; + int frame; + char *p = (str) ? str : buf; + + static const int MPEG_CLOCK_FREQ = 90000; + seconds = pts / MPEG_CLOCK_FREQ; + fracsec = pts % MPEG_CLOCK_FREQ; + + minutes = seconds / 60; + seconds = seconds % 60; + + hours = minutes / 60; + minutes = minutes % 60; + + frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps); + + snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, frame); + return p; +} + +static void print_debug_mask(int mask) +{ +#define MASK_OR_NOTHING (mask ? " | " : "") + if (mask & CX18_DBGFLG_WARN) { + mask &= ~CX18_DBGFLG_WARN; + printf("CX18_DBGFLG_WARN%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_INFO) { + mask &= ~CX18_DBGFLG_INFO; + printf("CX18_DBGFLG_INFO%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_API) { + mask &= ~CX18_DBGFLG_API; + printf("CX18_DBGFLG_API%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_DMA) { + mask &= ~CX18_DBGFLG_DMA; + printf("CX18_DBGFLG_DMA%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_IOCTL) { + mask &= ~CX18_DBGFLG_IOCTL; + printf("CX18_DBGFLG_IOCTL%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_FILE) { + mask &= ~CX18_DBGFLG_FILE; + printf("CX18_DBGFLG_FILE%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_I2C) { + mask &= ~CX18_DBGFLG_I2C; + printf("CX18_DBGFLG_I2C%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_IRQ) { + mask &= ~CX18_DBGFLG_IRQ; + printf("CX18_DBGFLG_IRQ%s", MASK_OR_NOTHING); + } + if (mask & CX18_DBGFLG_HIGHVOL) { + mask &= ~CX18_DBGFLG_HIGHVOL; + printf("CX18_DBGFLG_HIGHVOL%s", MASK_OR_NOTHING); + } + if (mask) + printf("0x%08x", mask); + printf("\n"); +} + +static int dowrite(const char *buf, const char *fn) +{ + FILE *f = fopen(fn, "w"); + if (f == NULL) { + printf("failed: %s\n", strerror(errno)); + return errno; + } + fprintf(f, buf); + fclose(f); + return 0; +} + +static char *doread(const char *fn) +{ + static char buf[1000]; + FILE *f = fopen(fn, "r"); + int s; + + if (f == NULL) { + printf("failed: %s\n", strerror(errno)); + return NULL; + } + s = fread(buf, 1, sizeof(buf) - 1, f); + buf[s] = 0; + fclose(f); + return buf; +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + + printf("ioctl %s ", name); + retVal = ioctl(fd, request, parm); + if (retVal < 0) + printf("failed: %s\n", strerror(errno)); + else + printf("ok\n"); + + return retVal; +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + char *subopts[] = { +#define SUB_VAL 0 + "val", +#define SUB_YUV_MODE 1 + "mode", +#define SUB_DIR 2 + "dir", +#define SUB_INPUT 3 + "input", +#define SUB_OUTPUT 4 + "output", + + NULL + }; + + int fd = -1; + + /* bitfield for OptSetCodec */ + + /* command args */ + char *device = strdup("/dev/video0"); /* -d device */ + int ch; + int yuv_mode = 0; + struct v4l2_routing route; /* audio_route */ + unsigned int gpio_out = 0x0; /* GPIO output data */ + unsigned int gpio_dir = 0x0; /* GPIO direction bits */ + int gpio_set_dir = 0; + int debug_level = 0; + __u32 reset = 0; + int new_debug_level, gdebug_level; + double timestamp; + char ptsstr[64]; + char short_options[26 * 2 * 2 + 1]; + + if (argc == 1) { + usage(); + return 0; + } + while (1) { + int option_index = 0; + int idx = 0; + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptSetDebugLevel:{ + debug_level = strtol(optarg, 0L, 0); + break; + } + case OptSetDevice: + device = strdup(optarg); + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + char dev = device[0]; + + sprintf(device, "/dev/video%c", dev); + } + break; + case OptSetAudioRoute: + subs = optarg; + while (*subs != '\0') { + switch (getsubopt(&subs, subopts, &value)) { + case SUB_INPUT: + if (value == NULL) { + printf + ("No value given to suboption <input>\n"); + usage(); + } + route.input = strtol(value, 0L, 0); + break; + case SUB_OUTPUT: + if (value == NULL) { + printf + ("No value given to suboption <output>\n"); + usage(); + } + route.output = strtol(value, 0L, 0); + break; + default: + printf + ("Invalid suboptions specified\n"); + usage(); + break; + } + } + break; + case OptReset: + reset = strtol(optarg, 0L, 0); + break; + case OptSetGPIO: + subs = optarg; + while (*subs != '\0') { + switch (getsubopt(&subs, subopts, &value)) { + case SUB_DIR: + if (value == NULL) { + fprintf(stderr, + "No value given to suboption <dir>\n"); + usage(); + exit(1); + } + gpio_dir = strtoul(value, 0L, 0); + gpio_set_dir = 1; + break; + case SUB_VAL: + if (value == NULL) { + fprintf(stderr, + "No value given to suboption <val>\n"); + usage(); + exit(1); + } + gpio_out = + (unsigned short)strtoul(value, 0L, 0); + break; + default: + fprintf(stderr, + "Invalid suboptions specified\n"); + usage(); + exit(1); + break; + } + } + break; + case ':': + fprintf(stderr, "Option `%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + fprintf(stderr, "Unknown argument `%s'\n", + argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + + fd = open(device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + free(device); + + /* Setting Opts */ + + if (options[OptSetAudioRoute]) + doioctl(fd, VIDIOC_INT_S_AUDIO_ROUTING, &route, "VIDIOC_INT_S_AUDIO_ROUTING"); + + if (options[OptSetGPIO]) { + struct v4l2_register reg; + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = CX18_REG_GPIO_DIR1; + reg.val = (unsigned)((gpio_dir & 0xffff) << 16); + if (gpio_set_dir && doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("GPIO dir 1 set to 0x%08llx\n", reg.val); + reg.reg = CX18_REG_GPIO_DIR2; + reg.val = (unsigned)(gpio_dir & 0xffff0000); + if (gpio_set_dir && doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("GPIO dir 2 set to 0x%08llx\n", reg.val); + reg.reg = CX18_REG_GPIO_OUT1; + reg.val = (unsigned)((gpio_dir & 0xffff) << 16) | (gpio_out & 0xffff); + if (doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("GPIO out 1 set to 0x%08llx\n", reg.val); + reg.reg = CX18_REG_GPIO_OUT2; + reg.val = (unsigned)(gpio_dir & 0xffff0000) | (gpio_out >> 16); + if (doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("GPIO out 2 set to 0x%08llx\n", reg.val); + } + + if (options[OptListGPIO]) { + struct v4l2_register reg; + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = CX18_REG_GPIO_IN; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) == 0) + printf("GPIO in: 0x%04llx\n", reg.val); + reg.reg = CX18_REG_GPIO_DIR1; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) == 0) + printf("GPIO dir: 0x%04llx\n", reg.val); + reg.reg = CX18_REG_GPIO_OUT1; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) == 0) + printf("GPIO out: 0x%04llx\n", reg.val); + } + + if (options[OptSetDebugLevel]) { + char buf[20]; + new_debug_level = debug_level; + + sprintf(buf, "%d", debug_level); + if (dowrite(buf, "/sys/module/cx18/parameters/debug") == 0) { + printf(" set debug level: "); + print_debug_mask(new_debug_level); + printf("\n"); + } + } + + if (options[OptGetDebugLevel]) { + char *buf; + + gdebug_level = 0; + buf = doread("/sys/module/cx18/parameters/debug"); + if (buf) { + gdebug_level = atol(buf); + printf(" debug level: "); + print_debug_mask(gdebug_level); + printf("\n"); + } + } + + if (options[OptReset]) + doioctl(fd, VIDIOC_INT_RESET, &reset, "VIDIOC_INT_RESET"); + + if (options[OptVersion]) + printf("cx18ctl version " CX18_VERSION "\n"); + + close(fd); + exit(0); +} diff --git a/v4l2-apps/util/ivtv-ctl.c b/v4l2-apps/util/ivtv-ctl.c new file mode 100644 index 000000000..9d3170785 --- /dev/null +++ b/v4l2-apps/util/ivtv-ctl.c @@ -0,0 +1,687 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo dot com> + + Cleanup and VBI and audio in/out options: + Copyright (C) 2004 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <math.h> +#include <linux/types.h> +#include <sys/klog.h> + +#include <linux/videodev2.h> +#include <linux/dvb/video.h> +#include <linux/dvb/audio.h> + +/* copied from ivtv-driver.h */ +#define IVTV_DBGFLG_WARN (1 << 0) +#define IVTV_DBGFLG_INFO (1 << 1) +#define IVTV_DBGFLG_MB (1 << 2) +#define IVTV_DBGFLG_IOCTL (1 << 3) +#define IVTV_DBGFLG_FILE (1 << 4) +#define IVTV_DBGFLG_DMA (1 << 5) +#define IVTV_DBGFLG_IRQ (1 << 6) +#define IVTV_DBGFLG_DEC (1 << 7) +#define IVTV_DBGFLG_YUV (1 << 8) +#define IVTV_DBGFLG_I2C (1 << 9) +/* Flag to turn on high volume debugging */ +#define IVTV_DBGFLG_HIGHVOL (1 << 10) + +/* Internals copied from media/v4l2-common.h */ +struct v4l2_routing { + __u32 input; + __u32 output; +}; +#define VIDIOC_INT_S_AUDIO_ROUTING _IOW('d', 109, struct v4l2_routing) + +#define VIDIOC_INT_RESET _IOW('d', 102, __u32) + +#include <linux/ivtv.h> + +/* GPIO */ +#define IVTV_REG_GPIO_IN_OFFSET (0x9008 + 0x02000000) +#define IVTV_REG_GPIO_OUT_OFFSET (0x900c + 0x02000000) +#define IVTV_REG_GPIO_DIR_OFFSET (0x9020 + 0x02000000) + +/* Options */ +enum Option { + OptSetDebugLevel = 'D', + OptSetDevice = 'd', + OptGetDebugLevel = 'e', + OptHelp = 'h', + OptSetGPIO = 'i', + OptListGPIO = 'I', + OptPassThrough = 'K', + OptFrameSync = 'k', + OptSetAudioRoute = 'v', + OptReset = 128, + OptSetYuvMode, + OptGetYuvMode, + OptSetAudioMute, + OptSetStereoMode, + OptSetBilingualMode, + OptLast = 256 +}; + +static char options[OptLast]; + +static int app_result; + +static struct option long_options[] = { + /* Please keep in alphabetical order of the short option. + That makes it easier to see which options are still free. */ + {"set-debug", required_argument, 0, OptSetDebugLevel}, + {"device", required_argument, 0, OptSetDevice}, + {"get-debug", no_argument, 0, OptGetDebugLevel}, + {"help", no_argument, 0, OptHelp}, + {"set-gpio", required_argument, 0, OptSetGPIO}, + {"list-gpio", no_argument, 0, OptListGPIO}, + {"passthrough", required_argument, 0, OptPassThrough}, + {"sync", no_argument, 0, OptFrameSync}, + {"audio-route", required_argument, 0, OptSetAudioRoute}, + {"reset", required_argument, 0, OptReset}, + {"get-yuv-mode", no_argument, 0, OptGetYuvMode}, + {"set-yuv-mode", required_argument, 0, OptSetYuvMode}, + {"set-audio-mute", required_argument, 0, OptSetAudioMute}, + {"set-stereo-mode", required_argument, 0, OptSetStereoMode}, + {"set-bilingual-mode", required_argument, 0, OptSetBilingualMode}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf(" -d, --device <dev> use device <dev> instead of /dev/video0\n"); + printf(" -h, --help display this help message\n"); + printf(" -K, --passthrough <mode>\n"); + printf(" set passthrough mode: 1 = on, 0 = off [IVTV_IOC_PASSTHROUGH]\n"); + printf(" --get-yuv-mode display the current yuv mode\n"); + printf(" --set-yuv-mode mode=<mode>\n"); + printf(" set the current yuv mode:\n"); + printf(" mode 0: interlaced (top transmitted first)\n"); + printf(" mode 1: interlaced (bottom transmitted first)\n"); + printf(" mode 2: progressive\n"); + printf(" mode 3: auto\n"); + printf(" --set-audio-mute <mute>\n"); + printf(" 0=enable audio during 1.5x and 0.5x playback\n"); + printf(" 1=mute audio during 1.5x and 0.5x playback\n"); + printf(" --set-stereo-mode <mode>\n"); + printf(" mode 0: playback stereo as stereo\n"); + printf(" mode 1: playback left stereo channel as mono\n"); + printf(" mode 2: playback right stereo channel as mono\n"); + printf(" mode 3: playback stereo as mono\n"); + printf(" mode 4: playback stereo as swapped stereo\n"); + printf(" --set-bilingual-mode <mode>\n"); + printf(" mode 0: playback bilingual as stereo\n"); + printf(" mode 1: playback left bilingual channel as mono\n"); + printf(" mode 2: playback right bilingual channel as mono\n"); + printf(" mode 3: playback bilingual as mono\n"); + printf(" mode 4: playback bilingual as swapped stereo\n"); + printf(" --reset <mask> reset the infrared receiver (1) or digitizer (2) [VIDIOC_INT_RESET]\n"); + printf("\n"); + printf("Expert options:\n"); + printf(" -D, --set-debug <level>\n"); + printf(" set the module debug variable\n"); + printf(" 1/0x0001: warning\n"); + printf(" 2/0x0002: info\n"); + printf(" 4/0x0004: mailbox\n"); + printf(" 8/0x0008: ioctl\n"); + printf(" 16/0x0010: file\n"); + printf(" 32/0x0020: dma\n"); + printf(" 64/0x0040: irq\n"); + printf(" 128/0x0080: decoder\n"); + printf(" 256/0x0100: yuv\n"); + printf(" 512/0x0200: i2c\n"); + printf(" 1024/0x0400: high volume\n"); + printf(" -e, --get-debug query the module debug variable\n"); + printf(" -I, --list-gpio\n"); + printf(" show GPIO input/direction/output bits\n"); + printf(" -i, --set-gpio [dir=<dir>,]val=<val>\n"); + printf(" set GPIO direction bits to <dir> and set output to <val>\n"); + printf(" -k, --sync test vsync's capabilities [VIDEO_GET_EVENT]\n"); + printf(" -v, --audio-route=input=<in>,output=<out>\n"); + printf(" set the audio input/output routing [VIDIOC_INT_S_AUDIO_ROUTING]\n"); + exit(0); +} + +static char *pts_to_string(char *str, unsigned long pts, float fps) +{ + static char buf[256]; + int hours, minutes, seconds, fracsec; + int frame; + char *p = (str) ? str : buf; + + static const int MPEG_CLOCK_FREQ = 90000; + seconds = pts / MPEG_CLOCK_FREQ; + fracsec = pts % MPEG_CLOCK_FREQ; + + minutes = seconds / 60; + seconds = seconds % 60; + + hours = minutes / 60; + minutes = minutes % 60; + + frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps); + + snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, frame); + return p; +} + +static void print_debug_mask(int mask) +{ +#define MASK_OR_NOTHING (mask ? " | " : "") + if (mask & IVTV_DBGFLG_WARN) { + mask &= ~IVTV_DBGFLG_WARN; + printf("IVTV_DBGFLG_WARN%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_INFO) { + mask &= ~IVTV_DBGFLG_INFO; + printf("IVTV_DBGFLG_INFO%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_MB) { + mask &= ~IVTV_DBGFLG_MB; + printf("IVTV_DBGFLG_MB%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_DMA) { + mask &= ~IVTV_DBGFLG_DMA; + printf("IVTV_DBGFLG_DMA%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_IOCTL) { + mask &= ~IVTV_DBGFLG_IOCTL; + printf("IVTV_DBGFLG_IOCTL%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_FILE) { + mask &= ~IVTV_DBGFLG_FILE; + printf("IVTV_DBGFLG_FILE%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_I2C) { + mask &= ~IVTV_DBGFLG_I2C; + printf("IVTV_DBGFLG_I2C%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_IRQ) { + mask &= ~IVTV_DBGFLG_IRQ; + printf("IVTV_DBGFLG_IRQ%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_DEC) { + mask &= ~IVTV_DBGFLG_DEC; + printf("IVTV_DBGFLG_DEC%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_YUV) { + mask &= ~IVTV_DBGFLG_YUV; + printf("IVTV_DBGFLG_YUV%s", MASK_OR_NOTHING); + } + if (mask & IVTV_DBGFLG_HIGHVOL) { + mask &= ~IVTV_DBGFLG_HIGHVOL; + printf("IVTV_DBGFLG_HIGHVOL%s", MASK_OR_NOTHING); + } + if (mask) + printf("0x%08x", mask); + printf("\n"); +} + +static int dowrite(const char *buf, const char *fn) +{ + FILE *f = fopen(fn, "w"); + if (f == NULL) { + printf("failed: %s\n", strerror(errno)); + return errno; + } + fprintf(f, buf); + fclose(f); + return 0; +} + +static char *doread(const char *fn) +{ + static char buf[1000]; + FILE *f = fopen(fn, "r"); + int s; + + if (f == NULL) { + printf("failed: %s\n", strerror(errno)); + return NULL; + } + s = fread(buf, 1, sizeof(buf) - 1, f); + buf[s] = 0; + fclose(f); + return buf; +} + +static const char *field2s(int val) +{ + switch (val) { + case V4L2_FIELD_ANY: + return "Any"; + case V4L2_FIELD_NONE: + return "None"; + case V4L2_FIELD_TOP: + return "Top"; + case V4L2_FIELD_BOTTOM: + return "Bottom"; + case V4L2_FIELD_INTERLACED: + return "Interlaced"; + case V4L2_FIELD_SEQ_TB: + return "Sequential Top-Bottom"; + case V4L2_FIELD_SEQ_BT: + return "Sequential Bottom-Top"; + case V4L2_FIELD_ALTERNATE: + return "Alternating"; + case V4L2_FIELD_INTERLACED_TB: + return "Interlaced Top-Bottom"; + case V4L2_FIELD_INTERLACED_BT: + return "Interlaced Bottom-Top"; + default: + return "Unknown"; + } +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + + printf("ioctl %s ", name); + retVal = ioctl(fd, request, parm); + if (retVal < 0) { + app_result = -1; + printf("failed: %s\n", strerror(errno)); + } + else + printf("ok\n"); + + return retVal; +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + char *subopts[] = { +#define SUB_VAL 0 + "val", +#define SUB_YUV_MODE 1 + "mode", +#define SUB_DIR 2 + "dir", +#define SUB_INPUT 3 + "input", +#define SUB_OUTPUT 4 + "output", + + NULL + }; + + int fd = -1; + + /* bitfield for OptSetCodec */ + + /* command args */ + char *device = strdup("/dev/video0"); /* -d device */ + int ch; + int yuv_mode = 0; + struct v4l2_routing route; /* audio_route */ + unsigned short gpio_out = 0x0; /* GPIO output data */ + unsigned short gpio_dir = 0x0; /* GPIO direction bits */ + int gpio_set_dir = 0; + int passthrough = 0; + int audio_mute = 0; + int stereo_mode = 0; + int bilingual_mode = 0; + int debug_level = 0; + __u32 reset = 0; + int new_debug_level, gdebug_level; + double timestamp; + char ptsstr[64]; + char short_options[26 * 2 * 2 + 1]; + + if (argc == 1) { + usage(); + return 0; + } + while (1) { + int option_index = 0; + int idx = 0; + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptSetYuvMode: + { + subs = optarg; + while (*subs != '\0') { + switch (getsubopt(&subs, subopts, &value)) { + case SUB_YUV_MODE: + if (value == NULL) { + fprintf(stderr, + "No value given to suboption <mode>\n"); + usage(); + return 1; + + } + yuv_mode = strtol(value, 0L, 0); + if (yuv_mode < 0 || yuv_mode > 3) { + fprintf(stderr, "invalid yuv mode\n"); + return 1; + } + break; + } + } + } + break; + case OptHelp: + usage(); + return 0; + case OptSetDebugLevel:{ + debug_level = strtol(optarg, 0L, 0); + break; + } + case OptSetDevice: + device = strdup(optarg); + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + char dev = device[0]; + + sprintf(device, "/dev/video%c", dev); + } + break; + case OptSetAudioRoute: + subs = optarg; + while (*subs != '\0') { + switch (getsubopt(&subs, subopts, &value)) { + case SUB_INPUT: + if (value == NULL) { + printf + ("No value given to suboption <input>\n"); + usage(); + } + route.input = strtol(value, 0L, 0); + break; + case SUB_OUTPUT: + if (value == NULL) { + printf + ("No value given to suboption <output>\n"); + usage(); + } + route.output = strtol(value, 0L, 0); + break; + default: + printf + ("Invalid suboptions specified\n"); + usage(); + break; + } + } + break; + case OptReset: + reset = strtol(optarg, 0L, 0); + break; + case OptPassThrough: + passthrough = strtol(optarg, 0L, 0); + break; + case OptSetAudioMute: + audio_mute = strtol(optarg, 0L, 0); + break; + case OptSetStereoMode: + stereo_mode = strtol(optarg, 0L, 0); + break; + case OptSetBilingualMode: + bilingual_mode = strtol(optarg, 0L, 0); + break; + case OptSetGPIO: + subs = optarg; + while (*subs != '\0') { + switch (getsubopt(&subs, subopts, &value)) { + case SUB_DIR: + if (value == NULL) { + fprintf(stderr, + "No value given to suboption <dir>\n"); + usage(); + exit(1); + } + gpio_dir = strtol(value, 0L, 0); + gpio_set_dir = 1; + break; + case SUB_VAL: + if (value == NULL) { + fprintf(stderr, + "No value given to suboption <val>\n"); + usage(); + exit(1); + } + gpio_out = (unsigned short)strtol(value, 0L, 0); + break; + default: + fprintf(stderr, + "Invalid suboptions specified\n"); + usage(); + exit(1); + break; + } + } + break; + case ':': + fprintf(stderr, "Option `%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + fprintf(stderr, "Unknown argument `%s'\n", + argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + + fd = open(device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + free(device); + + /* Setting Opts */ + + if (options[OptSetAudioRoute]) + doioctl(fd, VIDIOC_INT_S_AUDIO_ROUTING, &route, + "VIDIOC_INT_S_AUDIO_ROUTING"); + + if (options[OptFrameSync]) { + printf("ioctl: VIDEO_GET_EVENT\n"); + + for (;;) { + struct video_event ev; + int fps = 30; + v4l2_std_id std; + + if (ioctl(fd, VIDIOC_G_STD, &std) == 0) + fps = (std & V4L2_STD_525_60) ? 30 : 25; + if (ioctl(fd, VIDEO_GET_EVENT, &ev) < 0) { + fprintf(stderr, "ioctl: VIDEO_GET_EVENT failed\n"); + break; + } else if (ev.timestamp == 0) { + unsigned long long pts = 0, frame = 0; + struct timeval tv; + gettimeofday(&tv, NULL); + timestamp = + (double)tv.tv_sec + + ((double)tv.tv_usec / 1000000.0); + + ioctl(fd, VIDEO_GET_PTS, &pts); + ioctl(fd, VIDEO_GET_FRAME_COUNT, &frame); + pts_to_string(ptsstr, pts, fps); + printf("%10.6f: pts %-20s, %lld frames\n", + timestamp, ptsstr, frame); + } + } + } + + if (options[OptSetGPIO]) { + struct v4l2_register reg; + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = IVTV_REG_GPIO_DIR_OFFSET; + reg.val = gpio_dir; + if (gpio_set_dir && doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("GPIO dir set to 0x%04llx\n", reg.val); + reg.reg = IVTV_REG_GPIO_OUT_OFFSET; + reg.val = gpio_out; + if (doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("GPIO out set to 0x%04llx\n", reg.val); + } + + if (options[OptListGPIO]) { + struct v4l2_register reg; + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = IVTV_REG_GPIO_IN_OFFSET; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) == 0) + printf("GPIO in: 0x%04llx\n", reg.val); + reg.reg = IVTV_REG_GPIO_DIR_OFFSET; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) == 0) + printf("GPIO dir: 0x%04llx\n", reg.val); + reg.reg = IVTV_REG_GPIO_OUT_OFFSET; + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) == 0) + printf("GPIO out: 0x%04llx\n", reg.val); + } + + if (options[OptSetDebugLevel]) { + char buf[20]; + new_debug_level = debug_level; + + sprintf(buf, "%d", debug_level); + if (dowrite(buf, "/sys/module/ivtv/parameters/debug") == 0) { + printf(" set debug level: "); + print_debug_mask(new_debug_level); + printf("\n"); + } + } + + if (options[OptGetDebugLevel]) { + char *buf = doread("/sys/module/ivtv/parameters/debug"); + + gdebug_level = 0; + if (buf) { + gdebug_level = atol(buf); + printf(" debug level: "); + print_debug_mask(gdebug_level); + printf("\n"); + } + } + + if (options[OptPassThrough]) { + int source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; + + doioctl(fd, VIDEO_SELECT_SOURCE, (void *)source, + "IVTV_IOC_PASSTHROUGH"); + } + + if (options[OptSetAudioMute]) { + doioctl(fd, AUDIO_SET_MUTE, (void *)audio_mute, "AUDIO_SET_MUTE"); + } + + if (options[OptSetStereoMode]) { + doioctl(fd, AUDIO_CHANNEL_SELECT, + (void *)stereo_mode, "AUDIO_CHANNEL_SELECT"); + } + + if (options[OptSetBilingualMode]) { + doioctl(fd, AUDIO_BILINGUAL_CHANNEL_SELECT, + (void *)bilingual_mode, "AUDIO_BILINGUAL_CHANNEL_SELECT"); + } + + if (options[OptReset]) + doioctl(fd, VIDIOC_INT_RESET, &reset, "VIDIOC_INT_RESET"); + + if (options[OptSetYuvMode]) { + struct ivtv_dma_frame frame; + struct v4l2_format fmt; + const enum v4l2_field map[4] = { + V4L2_FIELD_INTERLACED_TB, + V4L2_FIELD_INTERLACED_BT, + V4L2_FIELD_NONE, + V4L2_FIELD_ANY, + }; + + printf("set yuv mode\n"); + memset(&frame, 0, sizeof(frame)); + frame.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (ioctl(fd, IVTV_IOC_DMA_FRAME, &frame) < 0) { + fprintf(stderr, "Unable to switch to user DMA YUV mode\n"); + exit(1); + } + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ioctl(fd, VIDIOC_G_FMT, &fmt); + fmt.fmt.pix.field = map[yuv_mode]; + doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + } + + if (options[OptGetYuvMode]) { + struct ivtv_dma_frame frame; + struct v4l2_format fmt; + + memset(&frame, 0, sizeof(frame)); + frame.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (ioctl(fd, IVTV_IOC_DMA_FRAME, &frame) < 0) { + fprintf(stderr, "Unable to switch to user DMA YUV mode\n"); + exit(1); + } + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT"); + printf("Current yuv_mode %d %s\n", fmt.fmt.pix.field, + field2s(fmt.fmt.pix.field)); + } + + close(fd); + exit(app_result); +} diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 198118ac6..d637e34d7 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -123,6 +123,8 @@ enum Option { static char options[OptLast]; +static int app_result; + static unsigned capabilities; typedef std::vector<struct v4l2_ext_control> ctrl_list; @@ -715,7 +717,7 @@ static void printcropcap(const struct v4l2_cropcap &cropcap) printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); } -static int printfmt(struct v4l2_format vfmt) +static void printfmt(struct v4l2_format vfmt) { const flag_def vbi_def[] = { { V4L2_VBI_UNSYNC, "unsynchronized" }, @@ -776,10 +778,7 @@ static int printfmt(struct v4l2_format vfmt) break; case V4L2_BUF_TYPE_PRIVATE: break; - default: - return -1; } - return 0; } static void print_video_formats(int fd, enum v4l2_buf_type type) @@ -999,10 +998,12 @@ static v4l2_std_id parse_ntsc(const char *ntsc) static int doioctl(int fd, int request, void *parm, const char *name) { - int retVal; + int retVal = ioctl(fd, request, parm); - if (!options[OptVerbose]) return ioctl(fd, request, parm); - retVal = ioctl(fd, request, parm); + if (retVal < 0) { + app_result = -1; + } + if (!options[OptVerbose]) return retVal; printf("%s: ", name); if (retVal < 0) printf("failed: %s\n", strerror(errno)); @@ -1068,9 +1069,7 @@ static void do_crop(int fd, unsigned int set_crop, struct v4l2_rect &vcrop, v4l2 struct v4l2_crop in_crop; in_crop.type = type; - if (ioctl(fd, VIDIOC_G_CROP, &in_crop) < 0) - fprintf(stderr, "ioctl: VIDIOC_G_CROP failed\n"); - else { + if (doioctl(fd, VIDIOC_G_CROP, &in_crop, "VIDIOC_G_CROP") == 0) { if (set_crop & CropWidth) in_crop.c.width = vcrop.width; if (set_crop & CropHeight) @@ -1079,8 +1078,7 @@ static void do_crop(int fd, unsigned int set_crop, struct v4l2_rect &vcrop, v4l2 in_crop.c.left = vcrop.left; if (set_crop & CropTop) in_crop.c.top = vcrop.top; - if (ioctl(fd, VIDIOC_S_CROP, &in_crop) < 0) - fprintf(stderr, "ioctl: VIDIOC_S_CROP failed\n"); + doioctl(fd, VIDIOC_S_CROP, &in_crop, "VIDIOC_S_CROP"); } } @@ -1603,43 +1601,35 @@ int main(int argc, char **argv) struct v4l2_tuner vt; memset(&vt, 0, sizeof(struct v4l2_tuner)); - if (ioctl(fd, VIDIOC_G_TUNER, &vt) < 0) { - fprintf(stderr, "ioctl: VIDIOC_G_TUNER failed\n"); - exit(1); + if (doioctl(fd, VIDIOC_G_TUNER, &vt, "VIDIOC_G_TUNER") == 0) { + vt.audmode = mode; + doioctl(fd, VIDIOC_S_TUNER, &vt, "VIDIOC_S_TUNER"); } - vt.audmode = mode; - doioctl(fd, VIDIOC_S_TUNER, &vt, "VIDIOC_S_TUNER"); } if (options[OptSetVideoFormat]) { struct v4l2_format in_vfmt; - printf("ioctl: VIDIOC_S_FMT\n"); + in_vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(fd, VIDIOC_G_FMT, &in_vfmt) < 0) - fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); - else { + if (doioctl(fd, VIDIOC_G_FMT, &in_vfmt, "VIDIOC_G_FMT") == 0) { if (set_fmts & FmtWidth) in_vfmt.fmt.pix.width = vfmt.fmt.pix.width; if (set_fmts & FmtHeight) in_vfmt.fmt.pix.height = vfmt.fmt.pix.height; - if (ioctl(fd, VIDIOC_S_FMT, &in_vfmt) < 0) - fprintf(stderr, "ioctl: VIDIOC_S_FMT failed\n"); + doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); } } if (options[OptSetVideoOutFormat]) { struct v4l2_format in_vfmt; - printf("ioctl: VIDIOC_S_FMT\n"); + in_vfmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - if (ioctl(fd, VIDIOC_G_FMT, &in_vfmt) < 0) - fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); - else { + if (doioctl(fd, VIDIOC_G_FMT, &in_vfmt, "VIDIOC_G_FMT") == 0) { if (set_fmts_out & FmtWidth) in_vfmt.fmt.pix.width = vfmt_out.fmt.pix.width; if (set_fmts_out & FmtHeight) in_vfmt.fmt.pix.height = vfmt_out.fmt.pix.height; - if (ioctl(fd, VIDIOC_S_FMT, &in_vfmt) < 0) - fprintf(stderr, "ioctl: VIDIOC_S_FMT failed\n"); + doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); } } @@ -1669,29 +1659,24 @@ int main(int argc, char **argv) if (options[OptSetOutputOverlayFormat]) { struct v4l2_format fmt; - memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; - if (ioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") < 0) - fprintf(stderr, "ioctl: VIDIOC_G_FMT failed\n"); - else { + if (doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") == 0) { if (set_overlay_fmt_out & FmtChromaKey) fmt.fmt.win.chromakey = overlay_fmt_out.fmt.win.chromakey; if (set_overlay_fmt_out & FmtGlobalAlpha) fmt.fmt.win.global_alpha = overlay_fmt_out.fmt.win.global_alpha; - if (ioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT") < 0) - fprintf(stderr, "ioctl: VIDIOC_S_FMT failed\n"); + doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); } } if (options[OptSetFBuf]) { struct v4l2_framebuffer fb; - if (ioctl(fd, VIDIOC_G_FBUF, &fb, "VIDIOC_G_FBUF") < 0) - fprintf(stderr, "ioctl: VIDIOC_G_FBUF failed\n"); - else { + + if (doioctl(fd, VIDIOC_G_FBUF, &fb, "VIDIOC_G_FBUF") == 0) { fb.flags &= ~set_fbuf; fb.flags |= fbuf.flags; - if (ioctl(fd, VIDIOC_S_FBUF, &fb) < 0) - fprintf(stderr, "ioctl: VIDIOC_S_FBUF failed\n"); + doioctl(fd, VIDIOC_S_FBUF, &fb, "VIDIOC_S_FBUF"); } } @@ -1763,69 +1748,53 @@ int main(int argc, char **argv) if (options[OptGetVideoFormat]) { vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (doioctl(fd, VIDIOC_G_FMT, &vfmt, "VIDIOC_G_FMT") == 0) - if (printfmt(vfmt) != 0) - fprintf(stderr, "error printing result\n"); + printfmt(vfmt); } if (options[OptGetVideoOutFormat]) { vfmt_out.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; if (doioctl(fd, VIDIOC_G_FMT, &vfmt_out, "VIDIOC_G_FMT") == 0) - if (printfmt(vfmt_out) != 0) - fprintf(stderr, "error printing result\n"); + printfmt(vfmt_out); } if (options[OptGetOverlayFormat]) { struct v4l2_format fmt; - memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; if (doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") == 0) - if (printfmt(fmt) != 0) - fprintf(stderr, "error printing result\n"); + printfmt(fmt); } if (options[OptGetOutputOverlayFormat]) { struct v4l2_format fmt; - memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; if (doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") == 0) - if (printfmt(fmt) != 0) - fprintf(stderr, "error printing result\n"); + printfmt(fmt); } if (options[OptGetSlicedVbiFormat]) { vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt, - "VIDIOC_G_FMT") == 0) - if (printfmt(vbi_fmt) != 0) - fprintf(stderr, - "error printing result\n"); + if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt, "VIDIOC_G_FMT") == 0) + printfmt(vbi_fmt); } if (options[OptGetSlicedVbiOutFormat]) { vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt_out, - "VIDIOC_G_FMT") == 0) - if (printfmt(vbi_fmt_out) != 0) - fprintf(stderr, - "error printing result\n"); + if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt_out, "VIDIOC_G_FMT") == 0) + printfmt(vbi_fmt_out); } if (options[OptGetVbiFormat]) { raw_fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE; - if (doioctl(fd, VIDIOC_G_FMT, &raw_fmt, - "VIDIOC_G_FMT") == 0) - if (printfmt(raw_fmt) != 0) - fprintf(stderr, - "error printing result\n"); + if (doioctl(fd, VIDIOC_G_FMT, &raw_fmt, "VIDIOC_G_FMT") == 0) + printfmt(raw_fmt); } if (options[OptGetVbiOutFormat]) { raw_fmt_out.type = V4L2_BUF_TYPE_VBI_OUTPUT; - if (doioctl(fd, VIDIOC_G_FMT, &raw_fmt_out, - "VIDIOC_G_FMT") == 0) - if (printfmt(raw_fmt_out) != 0) - fprintf(stderr, - "error printing result\n"); + if (doioctl(fd, VIDIOC_G_FMT, &raw_fmt_out, "VIDIOC_G_FMT") == 0) + printfmt(raw_fmt_out); } if (options[OptGetFBuf]) { @@ -2188,5 +2157,5 @@ int main(int argc, char **argv) } close(fd); - exit(0); + exit(app_result); } diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp index e55cbb2a8..e24ae7140 100644 --- a/v4l2-apps/util/v4l2-dbg.cpp +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -533,6 +533,9 @@ int main(int argc, char **argv) case V4L2_IDENT_CX23416: print_regs(fd, &get_reg, 0x02000000, 0x020000ff, stride); break; + case V4L2_IDENT_CX23418: + print_regs(fd, &get_reg, 0x02c40000, 0x02c409c7, stride); + break; default: /* By default print range 0-0xff */ print_regs(fd, &get_reg, 0, 0xff, stride); |