diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 15:36:25 +0200 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 15:36:25 +0200 |
commit | a8931ef380c92d121ae74ecfb03b2d63f72eea6f (patch) | |
tree | 980fb6b019e11e6cb1ece55b7faff184721a8053 /drivers/media | |
parent | [MTD] [NAND] Fix checkpatch warnings which showed up when atmel_nand.c moved (diff) | |
parent | Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6 (diff) | |
download | linux-a8931ef380c92d121ae74ecfb03b2d63f72eea6f.tar.xz linux-a8931ef380c92d121ae74ecfb03b2d63f72eea6f.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/media')
169 files changed, 13021 insertions, 1308 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index ddf57e135c6c..7a7803b5d497 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -89,8 +89,7 @@ config DVB_CORE config VIDEO_MEDIA tristate - default DVB_CORE || VIDEO_DEV - depends on DVB_CORE || VIDEO_DEV + default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV) comment "Multimedia drivers" diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 73f742c7e818..09a829d8a7e7 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -2,10 +2,7 @@ # Makefile for the kernel multimedia device drivers. # -obj-$(CONFIG_VIDEO_MEDIA) += common/ - -# Since hybrid devices are here, should be compiled if DVB and/or V4L -obj-$(CONFIG_VIDEO_MEDIA) += video/ +obj-y += common/ video/ obj-$(CONFIG_VIDEO_DEV) += radio/ obj-$(CONFIG_DVB_CORE) += dvb/ diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index a3485817e46c..8fa91f846d59 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2201,3 +2201,41 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { [0x25] = KEY_POWER, /* power */ }; EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); + +IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { + [0x20] = KEY_LIST, + [0x00] = KEY_POWER, + [0x28] = KEY_1, + [0x18] = KEY_2, + [0x38] = KEY_3, + [0x24] = KEY_4, + [0x14] = KEY_5, + [0x34] = KEY_6, + [0x2c] = KEY_7, + [0x1c] = KEY_8, + [0x3c] = KEY_9, + [0x12] = KEY_SUBTITLE, + [0x22] = KEY_0, + [0x32] = KEY_REWIND, + [0x3a] = KEY_SHUFFLE, + [0x02] = KEY_PRINT, + [0x11] = KEY_CHANNELDOWN, + [0x31] = KEY_CHANNELUP, + [0x0c] = KEY_ZOOM, + [0x1e] = KEY_VOLUMEDOWN, + [0x3e] = KEY_VOLUMEUP, + [0x0a] = KEY_MUTE, + [0x04] = KEY_AUDIO, + [0x26] = KEY_RECORD, + [0x06] = KEY_PLAY, + [0x36] = KEY_STOP, + [0x16] = KEY_PAUSE, + [0x2e] = KEY_REWIND, + [0x0e] = KEY_FASTFORWARD, + [0x30] = KEY_TEXT, + [0x21] = KEY_GREEN, + [0x01] = KEY_BLUE, + [0x08] = KEY_EPG, + [0x2a] = KEY_MENU, +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d); diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 5be85ff53e12..85482960d012 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -1,6 +1,6 @@ config MEDIA_ATTACH bool "Load and attach frontend and tuner driver modules as needed" - depends on DVB_CORE + depends on VIDEO_MEDIA depends on MODULES help Remove the static dependency of DVB card drivers on all @@ -19,10 +19,11 @@ config MEDIA_ATTACH config MEDIA_TUNER tristate - default DVB_CORE || VIDEO_DEV - depends on DVB_CORE || VIDEO_DEV - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE + default VIDEO_MEDIA && I2C + depends on VIDEO_MEDIA && I2C + select FW_LOADER if !MEDIA_TUNER_CUSTOMIZE && HOTPLUG + 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 @@ -46,7 +47,7 @@ if MEDIA_TUNER_CUSTOMIZE config MEDIA_TUNER_SIMPLE tristate "Simple tuner support" - depends on I2C + depends on VIDEO_MEDIA && I2C select MEDIA_TUNER_TDA9887 default m if MEDIA_TUNER_CUSTOMIZE help @@ -54,7 +55,7 @@ config MEDIA_TUNER_SIMPLE config MEDIA_TUNER_TDA8290 tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" - depends on I2C + depends on VIDEO_MEDIA && I2C select MEDIA_TUNER_TDA827X select MEDIA_TUNER_TDA18271 default m if MEDIA_TUNER_CUSTOMIZE @@ -63,21 +64,21 @@ config MEDIA_TUNER_TDA8290 config MEDIA_TUNER_TDA827X tristate "Philips TDA827X silicon tuner" - depends on DVB_CORE && I2C + 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 I2C + 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 I2C + depends on VIDEO_MEDIA && I2C default m if MEDIA_TUNER_CUSTOMIZE help Say Y here to include support for Philips TDA9885/6/7 @@ -85,67 +86,79 @@ config MEDIA_TUNER_TDA9887 config MEDIA_TUNER_TEA5761 tristate "TEA 5761 radio tuner (EXPERIMENTAL)" - depends on I2C && 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 I2C + 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 I2C + 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 I2C + 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 I2C + 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 I2C + 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 DVB_CORE && I2C + 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 I2C && FW_LOADER + 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 I2C + 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/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 236d9932fd92..55f7e6706297 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -20,6 +20,7 @@ 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/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c new file mode 100644 index 000000000000..0dc2bef9f6a3 --- /dev/null +++ b/drivers/media/common/tuners/mxl5005s.c @@ -0,0 +1,4110 @@ +/* + 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); + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); + + } + 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/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h new file mode 100644 index 000000000000..396db150bf0c --- /dev/null +++ b/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/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index e27a7620a32f..6fb5b4586569 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/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) @@ -487,16 +486,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); } /*---------------------------------------------------------------------*/ @@ -510,7 +508,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); @@ -542,7 +540,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; @@ -566,7 +564,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 */ @@ -583,7 +581,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 */ @@ -600,7 +598,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 */ @@ -617,7 +615,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 */ @@ -634,7 +632,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; @@ -650,11 +648,19 @@ int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) unsigned char *regs = priv->tda18271_regs; u8 val; - tda18271_lookup_map(fe, RF_CAL, freq, &val); + int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val); + /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range + * for frequencies above 61.1 MHz. In these cases, the internal RF + * tracking filters calibration mechanism is used. + * + * There is no need to warn the user about this. + */ + if (ret < 0) + goto fail; regs[R_EB14] = val; - - return 0; +fail: + return ret; } /* diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index b262100ae897..93063c6fbbf6 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -45,12 +45,28 @@ static inline int charge_pump_source(struct dvb_frontend *fe, int force) TDA18271_MAIN_PLL, force); } +static inline void tda18271_set_if_notch(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_MPD] &= ~0x80; /* IF notch = 0 */ + break; + case TDA18271_DIGITAL: + regs[R_MPD] |= 0x80; /* IF notch = 1 */ + break; + } +} + static int tda18271_channel_configuration(struct dvb_frontend *fe, struct tda18271_std_map_item *map, u32 freq, u32 bw) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int ret; u32 N; /* update TV broadcast parameters */ @@ -59,25 +75,18 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, regs[R_EP3] &= ~0x1f; /* clear std bits */ regs[R_EP3] |= (map->agc_mode << 3) | map->std; - /* set rfagc to high speed mode */ - regs[R_EP3] &= ~0x04; + if (priv->id == TDA18271HDC2) { + /* set rfagc to high speed mode */ + regs[R_EP3] &= ~0x04; + } /* set cal mode to normal */ regs[R_EP4] &= ~0x03; - /* update IF output level & IF notch frequency */ + /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ regs[R_EP4] |= (map->if_lvl << 2); - switch (priv->mode) { - case TDA18271_ANALOG: - regs[R_MPD] &= ~0x80; /* IF notch = 0 */ - break; - case TDA18271_DIGITAL: - regs[R_MPD] |= 0x80; /* IF notch = 1 */ - break; - } - /* update FM_RFn */ regs[R_EP4] &= ~0x80; regs[R_EP4] |= map->fm_rfn << 7; @@ -85,13 +94,18 @@ 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; /* --------------------------------------------------------------- */ /* disable Power Level Indicator */ regs[R_EP1] |= 0x40; + /* make sure thermometer is off */ + regs[R_TM] &= ~0x10; + /* frequency dependent parameters */ tda18271_calc_ir_measure(fe, &freq); @@ -121,7 +135,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; /* --------------------------------------------------------------- */ @@ -130,6 +146,7 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, switch (priv->role) { case TDA18271_MASTER: tda18271_calc_main_pll(fe, N); + tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 4); break; case TDA18271_SLAVE: @@ -137,11 +154,14 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, tda18271_write_regs(fe, R_CPD, 4); regs[R_MPD] = regs[R_CPD] & 0x7f; + tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 1); 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); @@ -153,14 +173,16 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, msleep(20); - /* set rfagc to normal speed mode */ - if (map->fm_rfn) - regs[R_EP3] &= ~0x04; - else - regs[R_EP3] |= 0x04; - tda18271_write_regs(fe, R_EP3, 1); - - return 0; + if (priv->id == TDA18271HDC2) { + /* set rfagc to normal speed mode */ + if (map->fm_rfn) + regs[R_EP3] &= ~0x04; + else + regs[R_EP3] |= 0x04; + ret = tda18271_write_regs(fe, R_EP3, 1); + } +fail: + return ret; } static int tda18271_read_thermometer(struct dvb_frontend *fe) @@ -213,11 +235,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); @@ -228,8 +252,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 * @@ -250,35 +274,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) @@ -389,7 +420,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; @@ -421,7 +452,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; @@ -447,7 +480,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; @@ -478,6 +513,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 */ @@ -486,13 +522,17 @@ static int tda18271_powerscan_init(struct dvb_frontend *fe) /* set cal mode to normal */ regs[R_EP4] &= ~0x03; - /* update IF output level & IF notch frequency */ + /* update IF output level */ 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 */ @@ -500,9 +540,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) @@ -521,7 +561,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; @@ -535,6 +575,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]; @@ -575,22 +617,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; } /* ------------------------------------------------------------------ */ @@ -599,6 +648,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) @@ -607,15 +657,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, @@ -623,6 +680,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 */ @@ -671,7 +729,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 */ @@ -719,45 +780,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); @@ -769,11 +841,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; } /* ------------------------------------------------------------------ */ @@ -837,7 +909,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; @@ -893,7 +965,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; @@ -905,16 +977,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) @@ -1095,10 +1168,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/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 2bc5eb368ea2..81a739365f8c 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -153,6 +153,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/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c index d30d2c9094d9..8555d9cf9051 100644 --- a/drivers/media/common/tuners/tda827x.c +++ b/drivers/media/common/tuners/tda827x.c @@ -418,13 +418,13 @@ static void tda827xa_lna_gain(struct dvb_frontend *fe, int high, unsigned char buf[] = {0x22, 0x01}; int arg; int gp_func; - struct i2c_msg msg = { .addr = priv->cfg->switch_addr, .flags = 0, - .buf = buf, .len = sizeof(buf) }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = sizeof(buf) }; if (NULL == priv->cfg) { dprintk("tda827x_config not defined, cannot set LNA gain!\n"); return; } + msg.addr = priv->cfg->switch_addr; if (priv->cfg->config) { if (high) dprintk("setting LNA to high gain\n"); diff --git a/drivers/media/common/tuners/tea5761.c b/drivers/media/common/tuners/tea5761.c index b93cdef9ac73..b23dadeecd05 100644 --- a/drivers/media/common/tuners/tea5761.c +++ b/drivers/media/common/tuners/tea5761.c @@ -295,7 +295,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/drivers/media/common/tuners/tea5767.c b/drivers/media/common/tuners/tea5767.c index f6e7d7ad8424..1f5646334a8f 100644 --- a/drivers/media/common/tuners/tea5767.c +++ b/drivers/media/common/tuners/tea5767.c @@ -373,14 +373,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: @@ -390,7 +390,7 @@ 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; } diff --git a/drivers/media/common/tuners/tuner-i2c.h b/drivers/media/common/tuners/tuner-i2c.h index 3ad6c8e0b04c..cb1c7141f0c6 100644 --- a/drivers/media/common/tuners/tuner-i2c.h +++ b/drivers/media/common/tuners/tuner-i2c.h @@ -170,4 +170,12 @@ __fail: \ __ret; \ }) +#define hybrid_tuner_report_instance_count(state) \ +({ \ + int __ret = 0; \ + if (state) \ + __ret = state->i2c_props.count; \ + __ret; \ +}) + #endif /* __TUNER_I2C_H__ */ diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index be8d903171b7..266c255cf0d8 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -1018,8 +1018,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/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index 9e9003cffc7f..0cbde17bfbb7 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -46,7 +46,7 @@ module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " "default firmware name\n"); -static LIST_HEAD(xc2028_list); +static LIST_HEAD(hybrid_tuner_instance_list); static DEFINE_MUTEX(xc2028_list_mutex); /* struct for storing firmware table */ @@ -68,12 +68,11 @@ struct firmware_properties { }; struct xc2028_data { - struct list_head xc2028_list; + struct list_head hybrid_tuner_instance_list; struct tuner_i2c_props i2c_props; int (*tuner_callback) (void *dev, int command, int arg); void *video_dev; - int count; __u32 frequency; struct firmware_description *firm; @@ -1072,20 +1071,19 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) mutex_lock(&xc2028_list_mutex); - priv->count--; - - if (!priv->count) { - list_del(&priv->xc2028_list); - + /* only perform final cleanup if this is the last instance */ + if (hybrid_tuner_report_instance_count(priv) == 1) { kfree(priv->ctrl.fname); - free_firmware(priv); - kfree(priv); - fe->tuner_priv = NULL; } + if (priv) + hybrid_tuner_release_state(priv); + mutex_unlock(&xc2028_list_mutex); + fe->tuner_priv = NULL; + return 0; } @@ -1150,7 +1148,7 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg) { struct xc2028_data *priv; - void *video_dev; + int instance; if (debug) printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); @@ -1163,48 +1161,40 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, return NULL; } - video_dev = cfg->i2c_adap->algo_data; - - if (debug) - printk(KERN_DEBUG "xc2028: video_dev =%p\n", video_dev); - mutex_lock(&xc2028_list_mutex); - list_for_each_entry(priv, &xc2028_list, xc2028_list) { - if (&priv->i2c_props.adap->dev == &cfg->i2c_adap->dev) { - video_dev = NULL; - if (debug) - printk(KERN_DEBUG "xc2028: reusing device\n"); - - break; - } - } - - if (video_dev) { - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (priv == NULL) { - mutex_unlock(&xc2028_list_mutex); - return NULL; - } - - priv->i2c_props.addr = cfg->i2c_addr; - priv->i2c_props.adap = cfg->i2c_adap; - priv->i2c_props.name = "xc2028"; - - priv->video_dev = video_dev; + instance = hybrid_tuner_request_state(struct xc2028_data, priv, + hybrid_tuner_instance_list, + cfg->i2c_adap, cfg->i2c_addr, + "xc2028"); + switch (instance) { + case 0: + /* memory allocation failure */ + goto fail; + break; + case 1: + /* new tuner instance */ priv->tuner_callback = cfg->callback; priv->ctrl.max_len = 13; mutex_init(&priv->lock); - list_add_tail(&priv->xc2028_list, &xc2028_list); - } - - fe->tuner_priv = priv; - priv->count++; + /* analog side (tuner-core) uses i2c_adap->algo_data. + * digital side is not guaranteed to have algo_data defined. + * + * digital side will always have fe->dvb defined. + * analog side (tuner-core) doesn't (yet) define fe->dvb. + */ + priv->video_dev = ((fe->dvb) && (fe->dvb->priv)) ? + fe->dvb->priv : cfg->i2c_adap->algo_data; - if (debug) - printk(KERN_DEBUG "xc2028: usage count is %i\n", priv->count); + fe->tuner_priv = priv; + break; + case 2: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, sizeof(xc2028_dvb_tuner_ops)); @@ -1217,6 +1207,11 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, mutex_unlock(&xc2028_list_mutex); return fe; +fail: + mutex_unlock(&xc2028_list_mutex); + + xc2028_dvb_release(fe); + return NULL; } EXPORT_SYMBOL(xc2028_attach); diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 43d35bdb221f..7cf4f5bdb2ec 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -177,6 +177,7 @@ static XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { {"FM Radio-INPUT1", 0x0208, 0x9002} }; +static int xc5000_is_firmware_loaded(struct dvb_frontend *fe); static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len); static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len); static void xc5000_TunerReset(struct dvb_frontend *fe); @@ -212,7 +213,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"); @@ -352,7 +353,7 @@ static int xc_SetTVStandard(struct xc5000_priv *priv, static int xc_shutdown(struct xc5000_priv *priv) { - return 0; + return XC_RESULT_SUCCESS; /* Fixme: cannot bring tuner back alive once shutdown * without reloading the driver modules. * return xc_write_reg(priv, XREG_POWER_DOWN, 0); @@ -685,6 +686,25 @@ static int xc5000_set_params(struct dvb_frontend *fe, return 0; } +static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + u16 id; + + ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id); + if (ret == XC_RESULT_SUCCESS) { + if (id == XC_PRODUCT_ID_FW_NOT_LOADED) + ret = XC_RESULT_RESET_FAILURE; + else + ret = XC_RESULT_SUCCESS; + } + + dprintk(1, "%s() returns %s id = 0x%x\n", __func__, + ret == XC_RESULT_SUCCESS ? "True" : "False", id); + return ret; +} + static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe); static int xc5000_set_analog_params(struct dvb_frontend *fe, @@ -693,7 +713,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if(priv->fwloaded == 0) + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) xc_load_fw_and_init_tuner(fe); dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", @@ -808,11 +828,10 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe) struct xc5000_priv *priv = fe->tuner_priv; int ret = 0; - if (priv->fwloaded == 0) { + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { ret = xc5000_fwupload(fe); if (ret != XC_RESULT_SUCCESS) return ret; - priv->fwloaded = 1; } /* Start the tuner self-calibration process */ @@ -852,7 +871,6 @@ static int xc5000_sleep(struct dvb_frontend *fe) return -EREMOTEIO; } else { - /* priv->fwloaded = 0; */ return XC_RESULT_SUCCESS; } } @@ -900,9 +918,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; @@ -916,6 +934,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. @@ -932,7 +951,6 @@ struct dvb_frontend * xc5000_attach(struct dvb_frontend *fe, cfg->i2c_address); printk(KERN_INFO "xc5000: Firmware has been loaded previously\n"); - priv->fwloaded = 1; break; case XC_PRODUCT_ID_FW_NOT_LOADED: printk(KERN_INFO @@ -940,7 +958,6 @@ struct dvb_frontend * xc5000_attach(struct dvb_frontend *fe, cfg->i2c_address); printk(KERN_INFO "xc5000: Firmware has not been loaded previously\n"); - priv->fwloaded = 0; break; default: printk(KERN_ERR diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 0ee80f9d19b8..c910715addc9 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -31,29 +31,31 @@ 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 +/* 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; diff --git a/drivers/media/common/tuners/xc5000_priv.h b/drivers/media/common/tuners/xc5000_priv.h index 13b2d19341da..a72a9887fe7f 100644 --- a/drivers/media/common/tuners/xc5000_priv.h +++ b/drivers/media/common/tuners/xc5000_priv.h @@ -30,7 +30,8 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; - u8 fwloaded; + + void *devptr; }; #endif diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 7b0ea3bdfafb..f9d087669d5d 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -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/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c index 449fb5c3d0b1..ae0d76a5d51d 100644 --- a/drivers/media/dvb/b2c2/flexcop-usb.c +++ b/drivers/media/dvb/b2c2/flexcop-usb.c @@ -379,7 +379,7 @@ static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) { - u16 frame_size = fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize; + u16 frame_size = le16_to_cpu(fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret; int buffer_offset = 0; diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index d1239b8342f8..7588db1319d0 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/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 diff --git a/drivers/media/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig index 3d778c5aba68..c03513b2ccae 100644 --- a/drivers/media/dvb/cinergyT2/Kconfig +++ b/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/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index f5010e8671b8..a824f3719f81 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -82,22 +82,22 @@ enum cinergyt2_ep1_cmd { struct dvbt_set_parameters_msg { uint8_t cmd; - uint32_t freq; + __le32 freq; uint8_t bandwidth; - uint16_t tps; + __le16 tps; uint8_t flags; } __attribute__((packed)); struct dvbt_get_status_msg { - uint32_t freq; + __le32 freq; uint8_t bandwidth; - uint16_t tps; + __le16 tps; uint8_t flags; - uint16_t gain; + __le16 gain; uint8_t snr; - uint32_t viterbi_error_rate; - uint32_t rs_error_rate; - uint32_t uncorrected_block_count; + __le32 viterbi_error_rate; + __le32 rs_error_rate; + __le32 uncorrected_block_count; uint8_t lock_bits; uint8_t prev_lock_bits; } __attribute__((packed)); @@ -136,6 +136,7 @@ struct cinergyt2 { wait_queue_head_t poll_wq; int pending_fe_events; int disconnect_pending; + unsigned int uncorrected_block_count; atomic_t inuse; void *streambuf; @@ -147,7 +148,7 @@ struct cinergyt2 { char phys[64]; struct delayed_work rc_query_work; int rc_input_event; - u32 rc_last_code; + __le32 rc_last_code; unsigned long last_event_jiffies; #endif }; @@ -160,7 +161,7 @@ enum { struct cinergyt2_rc_event { char type; - uint32_t value; + __le32 value; } __attribute__((packed)); static const uint32_t rc_keys[] = { @@ -619,8 +620,11 @@ static int cinergyt2_ioctl (struct inode *inode, struct file *file, { uint32_t unc_count; - unc_count = stat->uncorrected_block_count; - stat->uncorrected_block_count = 0; + if (mutex_lock_interruptible(&cinergyt2->sem)) + return -ERESTARTSYS; + unc_count = cinergyt2->uncorrected_block_count; + cinergyt2->uncorrected_block_count = 0; + mutex_unlock(&cinergyt2->sem); /* UNC are already converted to host byte order... */ return put_user(unc_count,(__u32 __user *) arg); @@ -769,7 +773,7 @@ static void cinergyt2_query_rc (struct work_struct *work) input_sync(cinergyt2->rc_input_dev); cinergyt2->rc_input_event = KEY_MAX; } - cinergyt2->rc_last_code = ~0; + cinergyt2->rc_last_code = cpu_to_le32(~0); } goto out; } @@ -780,7 +784,7 @@ static void cinergyt2_query_rc (struct work_struct *work) n, le32_to_cpu(rc_events[n].value), rc_events[n].type); if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC && - rc_events[n].value == ~0) { + rc_events[n].value == cpu_to_le32(~0)) { /* keyrepeat bit -> just repeat last rc_input_event */ } else { cinergyt2->rc_input_event = KEY_MAX; @@ -795,7 +799,7 @@ static void cinergyt2_query_rc (struct work_struct *work) if (cinergyt2->rc_input_event != KEY_MAX) { if (rc_events[n].value == cinergyt2->rc_last_code && - cinergyt2->rc_last_code != ~0) { + cinergyt2->rc_last_code != cpu_to_le32(~0)) { /* emit a key-up so the double event is recognized */ dprintk(1, "rc_input_event=%d UP\n", cinergyt2->rc_input_event); input_report_key(cinergyt2->rc_input_dev, @@ -829,7 +833,7 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) usb_make_path(cinergyt2->udev, cinergyt2->phys, sizeof(cinergyt2->phys)); strlcat(cinergyt2->phys, "/input0", sizeof(cinergyt2->phys)); cinergyt2->rc_input_event = KEY_MAX; - cinergyt2->rc_last_code = ~0; + cinergyt2->rc_last_code = cpu_to_le32(~0); INIT_DELAYED_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc); input_dev->name = DRIVER_NAME " remote control"; @@ -840,8 +844,8 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) input_dev->keycodesize = 0; input_dev->keycodemax = 0; input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = cinergyt2->udev->descriptor.idVendor; - input_dev->id.product = cinergyt2->udev->descriptor.idProduct; + input_dev->id.vendor = le16_to_cpu(cinergyt2->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(cinergyt2->udev->descriptor.idProduct); input_dev->id.version = 1; input_dev->dev.parent = &cinergyt2->udev->dev; @@ -889,18 +893,16 @@ static void cinergyt2_query (struct work_struct *work) char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS }; struct dvbt_get_status_msg *s = &cinergyt2->status; uint8_t lock_bits; - uint32_t unc; if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) return; - unc = s->uncorrected_block_count; lock_bits = s->lock_bits; cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s)); - unc += le32_to_cpu(s->uncorrected_block_count); - s->uncorrected_block_count = unc; + cinergyt2->uncorrected_block_count += + le32_to_cpu(s->uncorrected_block_count); if (lock_bits != s->lock_bits) { wake_up_interruptible(&cinergyt2->poll_wq); diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index 8cbdb0ec67e2..588fbe105c27 100644 --- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -910,15 +910,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: @@ -926,19 +932,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/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 56d871cfd7fc..c2334aef4143 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -168,7 +168,7 @@ struct dvb_net_priv { * stolen from eth.c out of the linux kernel, hacked for dvb-device * by Michael Holzt <kju@debian.org> */ -static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb, +static __be16 dvb_net_eth_type_trans(struct sk_buff *skb, struct net_device *dev) { struct ethhdr *eth; @@ -277,10 +277,10 @@ static int handle_one_ule_extension( struct dvb_net_priv *p ) if(ext_len >= 0) { p->ule_next_hdr += ext_len; if (!p->ule_bridged) { - p->ule_sndu_type = ntohs(*(unsigned short *)p->ule_next_hdr); + p->ule_sndu_type = ntohs(*(__be16 *)p->ule_next_hdr); p->ule_next_hdr += 2; } else { - p->ule_sndu_type = ntohs(*(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN))); + p->ule_sndu_type = ntohs(*(__be16 *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN))); /* This assures the extension handling loop will terminate. */ } } @@ -294,7 +294,7 @@ static int handle_one_ule_extension( struct dvb_net_priv *p ) if (ule_optional_ext_handlers[htype]) (void)ule_optional_ext_handlers[htype]( p ); p->ule_next_hdr += ext_len; - p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr-2) ); + p->ule_sndu_type = ntohs( *(__be16 *)(p->ule_next_hdr-2) ); /* * note: the length of the next header type is included in the * length of THIS optional extension header @@ -594,8 +594,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) /* Check for complete payload. */ if (priv->ule_sndu_remain <= 0) { /* Check CRC32, we've got it in our skb already. */ - unsigned short ulen = htons(priv->ule_sndu_len); - unsigned short utype = htons(priv->ule_sndu_type); + __be16 ulen = htons(priv->ule_sndu_len); + __be16 utype = htons(priv->ule_sndu_type); const u8 *tail; struct kvec iov[3] = { { &ulen, sizeof ulen }, diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 4c1cff9feb2e..f00a0eb40420 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/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 diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 346223856f59..c4d40fe01d57 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -111,8 +111,8 @@ static int bristol_tuner_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1); s8 a; int if1=1220; - if (adap->dev->udev->descriptor.idVendor == USB_VID_HAUPPAUGE && - adap->dev->udev->descriptor.idProduct == USB_PID_HAUPPAUGE_NOVA_T_500_2) { + if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && + adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_500_2)) { if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a; } return dvb_attach(mt2060_attach,adap->fe, tun_i2c,&bristol_mt2060_config[adap->id], @@ -402,8 +402,8 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) { struct usb_device_descriptor *desc = &adap->dev->udev->descriptor; - if (desc->idVendor == USB_VID_PINNACLE && - desc->idProduct == USB_PID_PINNACLE_EXPRESSCARD_320CX) + if (desc->idVendor == cpu_to_le16(USB_VID_PINNACLE) && + desc->idProduct == cpu_to_le16(USB_PID_PINNACLE_EXPRESSCARD_320CX)) dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); else dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); @@ -845,8 +845,8 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) struct i2c_adapter *tun_i2c; s8 a; int if1=1220; - if (adap->dev->udev->descriptor.idVendor == USB_VID_HAUPPAUGE && - adap->dev->udev->descriptor.idProduct == USB_PID_HAUPPAUGE_NOVA_T_STICK) { + if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && + adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_STICK)) { if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a; } if (st->is_dib7000pc) @@ -990,11 +990,12 @@ static struct dib7000p_config dib7070p_dib7000p_config = { /* STK7070P */ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) { - if (adap->dev->udev->descriptor.idVendor == USB_VID_PINNACLE && - adap->dev->udev->descriptor.idProduct == USB_PID_PINNACLE_PCTV72E) - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); + struct usb_device_descriptor *p = &adap->dev->udev->descriptor; + if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && + p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); else - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c b/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c index e1112e39fb63..733a7ff7b207 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c @@ -127,7 +127,7 @@ int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, if ((*pos + hx->len + 4) >= fw->size) return -EINVAL; - hx->addr = le16_to_cpu( *((u16 *) &b[1]) ); + hx->addr = b[1] | (b[2] << 8); hx->type = b[3]; if (hx->type == 0x04) { diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 0a8ac64a4e33..037f7ffb47b2 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -47,6 +47,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, return -EINVAL; } + msleep(1); /* avoid I2C errors */ + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, value, index, rbuf, rlen, 2000); } @@ -92,16 +94,6 @@ static struct i2c_algorithm gl861_i2c_algo = { }; /* Callbacks for DVB USB */ -static int gl861_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, - int *cold) -{ - *cold = 0; - - return 0; -} - static struct zl10353_config gl861_zl10353_config = { .demod_address = 0x0f, .no_tuner = 1, @@ -172,7 +164,6 @@ static struct dvb_usb_device_properties gl861_properties = { .size_of_priv = 0, - .identify_state = gl861_identify_state, .num_adapters = 1, .adapter = {{ @@ -194,13 +185,15 @@ static struct dvb_usb_device_properties gl861_properties = { .num_device_descs = 2, .devices = { - { "MSI Mega Sky 55801 DVB-T USB2.0", - { &gl861_table[0], NULL }, - { NULL }, + { + .name = "MSI Mega Sky 55801 DVB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &gl861_table[0], NULL }, }, - { "A-LINK DTU DVB-T USB2.0", - { &gl861_table[1], NULL }, - { NULL }, + { + .name = "A-LINK DTU DVB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &gl861_table[1], NULL }, }, } }; diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c index 9a942afaf0af..2653120673b7 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/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/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index a12e6f784fda..54626a0dbf68 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -16,6 +16,7 @@ #include "qt1010.h" #include "tda1004x.h" #include "tda827x.h" +#include <asm/unaligned.h> /* debug */ static int dvb_usb_m920x_debug; @@ -347,13 +348,13 @@ static int m920x_firmware_download(struct usb_device *udev, const struct firmwar for (pass = 0; pass < 2; pass++) { for (i = 0; i + (sizeof(u16) * 3) < fw->size;) { - value = le16_to_cpu(*(u16 *)(fw->data + i)); + value = get_unaligned_le16(fw->data + i); i += sizeof(u16); - index = le16_to_cpu(*(u16 *)(fw->data + i)); + index = get_unaligned_le16(fw->data + i); i += sizeof(u16); - size = le16_to_cpu(*(u16 *)(fw->data + i)); + size = get_unaligned_le16(fw->data + i); i += sizeof(u16); if (pass == 1) { diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index 9e7653bb3b66..118aab1a3e54 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -107,7 +107,7 @@ static struct dvb_usb_device_properties umt_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 20, + .count = MAX_NO_URBS_FOR_DATA_STREAM, .endpoint = 0x06, .u = { .bulk = { diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 6d2384605927..c20553c4da1f 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -30,7 +30,7 @@ config DVB_CX24123 A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_MT312 - tristate "Zarlink VP310/MT312 based" + tristate "Zarlink VP310/MT312/ZL10313 based" depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help @@ -97,7 +97,7 @@ comment "DVB-T (terrestrial) frontends" 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 @@ -110,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 @@ -144,7 +144,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 @@ -211,7 +211,7 @@ config DVB_DIB7000P config DVB_TDA10048 tristate "Philips TDA10048HN based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && HOTPLUG default m if DVB_FE_CUSTOMISE select FW_LOADER help @@ -253,7 +253,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 @@ -268,7 +268,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 @@ -281,7 +281,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 @@ -297,7 +297,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 diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c index 084a280c2d7f..03900d241a76 100644 --- a/drivers/media/dvb/frontends/au8522.c +++ b/drivers/media/dvb/frontends/au8522.c @@ -463,10 +463,13 @@ static int au8522_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; dprintk("%s(frequency=%d)\n", __func__, p->frequency); - state->current_frequency = p->frequency; + if ((state->current_frequency == p->frequency) && + (state->current_modulation == p->u.vsb.modulation)) + return 0; au8522_enable_modulation(fe, p->u.vsb.modulation); @@ -476,11 +479,16 @@ static int au8522_set_frontend(struct dvb_frontend *fe, 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); + ret = fe->ops.tuner_ops.set_params(fe, p); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } + if (ret < 0) + return ret; + + state->current_frequency = p->frequency; + return 0; } @@ -498,6 +506,16 @@ static int au8522_init(struct dvb_frontend *fe) return 0; } +static int au8522_sleep(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + state->current_frequency = 0; + + return 0; +} + static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct au8522_state *state = fe->demodulator_priv; @@ -509,10 +527,8 @@ static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) 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; + if ((reg & 0x03) == 0x03) + *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; } else { dprintk("%s() Checking QAM\n", __func__); reg = au8522_readreg(state, 0x4541); @@ -672,6 +688,7 @@ static struct dvb_frontend_ops au8522_ops = { }, .init = au8522_init, + .sleep = au8522_sleep, .i2c_gate_ctrl = au8522_i2c_gate_ctrl, .set_frontend = au8522_set_frontend, .get_frontend = au8522_get_frontend, diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h index 786e37d33889..3eedfdf505bc 100644 --- a/drivers/media/dvb/frontends/dib0070.h +++ b/drivers/media/dvb/frontends/dib0070.h @@ -37,7 +37,20 @@ struct dib0070_config { u8 flip_chip; }; -extern struct dvb_frontend * dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); +#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) +extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct dib0070_config *cfg); +#else +static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct dib0070_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, uint8_t open); extern u16 dib0070_wbd_offset(struct dvb_frontend *); diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h index 081bd81f3da2..07c4d12ed5b7 100644 --- a/drivers/media/dvb/frontends/dib7000p.h +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -37,7 +37,20 @@ struct dib7000p_config { #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/drivers/media/dvb/frontends/itd1000.c b/drivers/media/dvb/frontends/itd1000.c index 04c562ccf990..600dad6b41ea 100644 --- a/drivers/media/dvb/frontends/itd1000.c +++ b/drivers/media/dvb/frontends/itd1000.c @@ -195,7 +195,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/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c index 081ca3398c76..5ac9b15920f8 100644 --- a/drivers/media/dvb/frontends/mt312.c +++ b/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/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h index de796eab3911..29e3bb5496b8 100644 --- a/drivers/media/dvb/frontends/mt312.h +++ b/drivers/media/dvb/frontends/mt312.h @@ -37,10 +37,10 @@ struct mt312_config { }; #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/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index c7b5785f81f2..5ed32544de39 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -126,7 +126,7 @@ static int or51132_readreg(struct or51132_state *state, u8 reg) reg, err); return -EREMOTEIO; } - return le16_to_cpup((u16*)buf); + return buf[0] | (buf[1] << 8); } static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) @@ -140,9 +140,9 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware dprintk("Firmware is %Zd bytes\n",fw->size); /* Get size of firmware A and B */ - firmwareAsize = le32_to_cpu(*((u32*)fw->data)); + firmwareAsize = le32_to_cpu(*((__le32*)fw->data)); dprintk("FirmwareA is %i bytes\n",firmwareAsize); - firmwareBsize = le32_to_cpu(*((u32*)(fw->data+4))); + firmwareBsize = le32_to_cpu(*((__le32*)(fw->data+4))); dprintk("FirmwareB is %i bytes\n",firmwareBsize); /* Upload firmware */ diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 17556183e871..35435bef8e79 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -63,6 +63,7 @@ struct stv0299_state { u32 symbol_rate; fe_code_rate_t fec_inner; int errmode; + u32 ucblocks; }; #define STATUS_BER 0 @@ -501,8 +502,10 @@ static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) { struct stv0299_state* state = fe->demodulator_priv; - if (state->errmode != STATUS_BER) return 0; - *ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + if (state->errmode != STATUS_BER) + return -ENOSYS; + + *ber = stv0299_readreg(state, 0x1e) | (stv0299_readreg(state, 0x1d) << 8); return 0; } @@ -540,8 +543,12 @@ static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { struct stv0299_state* state = fe->demodulator_priv; - if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0; - else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + if (state->errmode != STATUS_UCBLOCKS) + return -ENOSYS; + + state->ucblocks += stv0299_readreg(state, 0x1e); + state->ucblocks += (stv0299_readreg(state, 0x1d) << 8); + *ucblocks = state->ucblocks; return 0; } diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c index 0727b80bc4d2..c6ff5b82ff80 100644 --- a/drivers/media/dvb/frontends/tda10023.c +++ b/drivers/media/dvb/frontends/tda10023.c @@ -116,9 +116,12 @@ static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) int ret; ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) - printk("DVB: TDA10023: %s: readreg error (ret == %i)\n", - __func__, ret); + if (ret != 2) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error " + "(reg == 0x%02x, ret == %i)\n", + num, __func__, reg, ret); + } return b1[0]; } @@ -129,11 +132,12 @@ static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) int ret; ret = i2c_transfer (state->i2c, &msg, 1); - if (ret != 1) - printk("DVB: TDA10023(%d): %s, writereg error " + if (ret != 1) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error " "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", - state->frontend.dvb->num, __func__, reg, data, ret); - + num, __func__, reg, data, ret); + } return (ret != 1) ? -EREMOTEIO : 0; } @@ -464,7 +468,7 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, int i; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 49973846373e..a0d638653567 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1248,11 +1248,14 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; + int id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); - if (!state) + if (!state) { + printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); return NULL; + } /* setup the state */ state->config = config; @@ -1260,7 +1263,15 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, state->demod_type = TDA1004X_DEMOD_TDA10045; /* check if the demod is there */ - if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) { + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + + if (id != 0x25) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); return NULL; } @@ -1307,11 +1318,14 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; + int id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); - if (!state) + if (!state) { + printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); return NULL; + } /* setup the state */ state->config = config; @@ -1319,7 +1333,14 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, state->demod_type = TDA1004X_DEMOD_TDA10046; /* check if the demod is there */ - if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) { + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + if (id != 0x46) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); return NULL; } diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index ae882432dd3d..07643e010093 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/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 @@ -100,6 +101,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_BUDGET_CORE && I2C + depends on INPUT # due to IR select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE @@ -123,6 +125,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/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 747e7f1a6267..f05d43d8b5cf 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -51,6 +51,7 @@ #include <linux/crc32.h> #include <linux/i2c.h> #include <linux/kthread.h> +#include <asm/unaligned.h> #include <asm/system.h> @@ -1461,9 +1462,9 @@ static int check_firmware(struct av7110* av7110) ptr += 4; /* check dpram file */ - crc = ntohl(*(u32*) ptr); + crc = get_unaligned_be32(ptr); ptr += 4; - len = ntohl(*(u32*) ptr); + len = get_unaligned_be32(ptr); ptr += 4; if (len >= 512) { printk("dvb-ttpci: dpram file is way too big.\n"); @@ -1478,9 +1479,9 @@ static int check_firmware(struct av7110* av7110) ptr += len; /* check root file */ - crc = ntohl(*(u32*) ptr); + crc = get_unaligned_be32(ptr); ptr += 4; - len = ntohl(*(u32*) ptr); + len = get_unaligned_be32(ptr); ptr += 4; if (len <= 200000 || len >= 300000 || diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index 3e6b650fbb81..ec55a968f204 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -965,8 +965,9 @@ static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) { - int i, n; + unsigned i, n; int progressive = 0; + int match = 0; dprintk(2, "av7110:%p, \n", av7110); @@ -975,12 +976,31 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len return -EBUSY; } - for (i = 0; i < len - 5; i++) { - /* get progressive flag from picture extension */ - if (buf[i] == 0x00 && buf[i+1] == 0x00 && - buf[i+2] == 0x01 && (unsigned char)buf[i+3] == 0xb5 && - (buf[i+4] & 0xf0) == 0x10) - progressive = buf[i+5] & 0x08; + /* search in buf for instances of 00 00 01 b5 1? */ + for (i = 0; i < len; i++) { + unsigned char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (match == 5) { + progressive = c & 0x08; + match = 0; + } + if (c == 0x00) { + match = (match == 1 || match == 2) ? 2 : 1; + continue; + } + switch (match++) { + case 2: if (c == 0x01) + continue; + break; + case 3: if (c == 0xb5) + continue; + break; + case 4: if ((c & 0xf0) == 0x10) + continue; + break; + } + match = 0; } /* setting n always > 1, fixes problems when playing stillframes diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 9d81074b31df..3a3f5279e927 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -427,6 +427,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) if (err) { printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", __func__, type); + av7110->arm_errors++; return -ETIMEDOUT; } msleep(1); @@ -853,10 +854,8 @@ static osd_raw_window_t bpp2bit[8] = { static inline int WaitUntilBmpLoaded(struct av7110 *av7110) { - int ret = wait_event_interruptible_timeout(av7110->bmpq, + int ret = wait_event_timeout(av7110->bmpq, av7110->bmp_state != BMP_LOADING, 10*HZ); - if (ret == -ERESTARTSYS) - return ret; if (ret == 0) { printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", ret, av7110->bmp_state); diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 732ce4de512e..5d2d81ab2371 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -552,7 +552,7 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, u16 csum = 0, cc; int i; for (i = 0; i < len; i += 2) - csum ^= le16_to_cpup((u16 *) (muxpack + i)); + csum ^= le16_to_cpup((__le16 *) (muxpack + i)); if (csum) { printk("%s: muxpack with incorrect checksum, ignoring\n", __func__); diff --git a/drivers/media/dvb/ttusb-dec/Kconfig b/drivers/media/dvb/ttusb-dec/Kconfig index 83611012ef34..a23cc0aa17d3 100644 --- a/drivers/media/dvb/ttusb-dec/Kconfig +++ b/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/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index 42eee04daa5d..fefdc05e84ac 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -343,7 +343,7 @@ static int ttusb_dec_get_stb_state (struct ttusb_dec *dec, unsigned int *mode, u8 c[COMMAND_PACKET_SIZE]; int c_length; int result; - unsigned int tmp; + __be32 tmp; dprintk("%s\n", __func__); @@ -398,9 +398,9 @@ static void ttusb_dec_set_pids(struct ttusb_dec *dec) 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - u16 pcr = htons(dec->pid[DMX_PES_PCR]); - u16 audio = htons(dec->pid[DMX_PES_AUDIO]); - u16 video = htons(dec->pid[DMX_PES_VIDEO]); + __be16 pcr = htons(dec->pid[DMX_PES_PCR]); + __be16 audio = htons(dec->pid[DMX_PES_AUDIO]); + __be16 video = htons(dec->pid[DMX_PES_VIDEO]); dprintk("%s\n", __func__); @@ -435,7 +435,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) case 0x01: { /* VideoStream */ int prebytes = pva[5] & 0x03; int postbytes = (pva[5] & 0x0c) >> 2; - u16 v_pes_payload_length; + __be16 v_pes_payload_length; if (output_pva) { dec->video_filter->feed->cb.ts(pva, length, NULL, 0, @@ -1006,7 +1006,7 @@ static int ttusb_dec_start_sec_feed(struct dvb_demux_feed *dvbdmxfeed) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - u16 pid; + __be16 pid; u8 c[COMMAND_PACKET_SIZE]; int c_length; int result; @@ -1278,9 +1278,10 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) u8 *firmware = NULL; size_t firmware_size = 0; u16 firmware_csum = 0; - u16 firmware_csum_ns; - u32 firmware_size_nl; - u32 crc32_csum, crc32_check, tmp; + __be16 firmware_csum_ns; + __be32 firmware_size_nl; + u32 crc32_csum, crc32_check; + __be32 tmp; const struct firmware *fw_entry = NULL; dprintk("%s\n", __func__); @@ -1306,7 +1307,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) valid. */ crc32_csum = crc32(~0L, firmware, 56) ^ ~0L; memcpy(&tmp, &firmware[56], 4); - crc32_check = htonl(tmp); + crc32_check = ntohl(tmp); if (crc32_csum != crc32_check) { printk("%s: crc32 check of DSP code failed (calculated " "0x%08x != 0x%08x in file), file invalid.\n", @@ -1627,7 +1628,7 @@ static int ttusb_dec_probe(struct usb_interface *intf, usb_set_intfdata(intf, (void *)dec); - switch (le16_to_cpu(id->idProduct)) { + switch (id->idProduct) { case 0x1006: ttusb_dec_set_model(dec, TTUSB_DEC3000S); break; @@ -1652,7 +1653,7 @@ static int ttusb_dec_probe(struct usb_interface *intf, ttusb_dec_init_dvb(dec); dec->adapter.priv = dec; - switch (le16_to_cpu(id->idProduct)) { + switch (id->idProduct) { case 0x1006: dec->fe = ttusbdecfe_dvbs_attach(&fe_config); break; diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c index eb5eaeccd7c4..443af24097f3 100644 --- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c +++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c @@ -86,7 +86,7 @@ static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend* fe, struct dvb_fron 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }; - u32 freq = htonl(p->frequency / 1000); + __be32 freq = htonl(p->frequency / 1000); memcpy(&b[4], &freq, sizeof (u32)); state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); @@ -117,10 +117,10 @@ static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend* fe, struct dvb_fron 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - u32 freq; - u32 sym_rate; - u32 band; - u32 lnb_voltage; + __be32 freq; + __be32 sym_rate; + __be32 band; + __be32 lnb_voltage; freq = htonl(p->frequency + (state->hi_band ? LOF_HI : LOF_LO)); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index fe743aa7f645..5ccb0aeca8cc 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -44,6 +44,10 @@ config VIDEO_TVEEPROM tristate depends on I2C +config VIDEO_TUNER + tristate + depends on MEDIA_TUNER + # # Multimedia Video device configuration # @@ -690,7 +694,7 @@ config VIDEO_MXB tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" depends on PCI && VIDEO_V4L1 && I2C select VIDEO_SAA7146_VV - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO @@ -789,6 +793,14 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS && USB +config USB_VIDEO_CLASS + tristate "USB Video Class (UVC)" + ---help--- + Support for the USB Video Class (UVC). Currently only video + input devices, such as webcams, are supported. + + For more information see: <http://linux-uvc.berlios.de/> + source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" @@ -897,7 +909,7 @@ endif # V4L_USB_DRIVERS config SOC_CAMERA tristate "SoC camera support" - depends on VIDEO_V4L2 + depends on VIDEO_V4L2 && HAS_DMA select VIDEOBUF_DMA_SG help SoC Camera is a common API to several cameras, not connecting @@ -906,7 +918,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 @@ -921,7 +933,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/drivers/media/video/Makefile b/drivers/media/video/Makefile index a352c6e31f0c..ecbbfaab24d5 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -84,7 +84,7 @@ obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o obj-$(CONFIG_VIDEO_DPC) += dpc7146.o obj-$(CONFIG_TUNER_3036) += tuner-3036.o -obj-$(CONFIG_MEDIA_TUNER) += tuner.o +obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o @@ -136,6 +136,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ +obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig index cab277fafa63..52b2491581a8 100644 --- a/drivers/media/video/au0828/Kconfig +++ b/drivers/media/video/au0828/Kconfig @@ -1,8 +1,9 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on VIDEO_DEV && I2C && INPUT && DVB_CORE + 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--- diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index a2a6983444fa..898e12395e7c 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -77,8 +77,14 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) /* Make sure we support the board model */ switch (tv.model) { + case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ + case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; default: printk(KERN_WARNING "%s: warning: " @@ -175,6 +181,18 @@ struct usb_device_id au0828_usb_id_table [] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, { USB_DEVICE(0x0fe9, 0xd620), .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { USB_DEVICE(0x2040, 0x7210), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7217), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721b), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721f), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7280), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x0fd9, 0x0008), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index 1371b4e4b5f1..c6d470590380 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -337,12 +337,10 @@ int au0828_dvb_register(struct au0828_dev *dev) dvb->frontend = dvb_attach(au8522_attach, &hauppauge_hvr950q_config, &dev->i2c_adap); - if (dvb->frontend != NULL) { - hauppauge_hvr950q_tunerconfig.priv = dev; + if (dvb->frontend != NULL) dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); - } + &hauppauge_hvr950q_tunerconfig, dev); break; default: printk(KERN_WARNING "The frontend of your DVB/ATSC card " @@ -355,12 +353,6 @@ int au0828_dvb_register(struct au0828_dev *dev) return -1; } - /* Put the analog decoder in standby to keep it quiet */ - au0828_call_i2c_clients(dev, TUNER_SET_STANDBY, NULL); - - if (dvb->frontend->ops.analog_ops.standby) - dvb->frontend->ops.analog_ops.standby(dvb->frontend); - /* register everything */ ret = dvb_register(dev); if (ret < 0) { diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index 7431ef6de9f1..24a34fc1f2b3 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -1,12 +1,13 @@ 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 select VIDEOBUF_DMA_SG select VIDEO_IR - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index f20a01cfc73e..8ef0424c26c4 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -34,6 +34,7 @@ #include <linux/firmware.h> #include <net/checksum.h> +#include <asm/unaligned.h> #include <asm/io.h> #include "bttvp.h" @@ -3858,7 +3859,7 @@ static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256]) ee += i; /* found a valid descriptor */ - type = be16_to_cpup((u16*)(ee+4)); + type = get_unaligned_be16((__be16 *)(ee+4)); switch(type) { /* 848 based */ @@ -3918,7 +3919,7 @@ static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256]) btv->c.nr, type); break; } - serial = be32_to_cpup((u32*)(ee+6)); + serial = get_unaligned_be32((__be32 *)(ee+6)); } printk(KERN_INFO "bttv%d: osprey eeprom: card=%d '%s' serial=%u\n", diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 2ca3e9cfb2bb..0165aac533bf 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2613,7 +2613,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/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index e5979f77504c..0af586876e72 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -48,7 +48,7 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, { u32 instructions,line,todo; struct scatterlist *sg; - u32 *rp; + __le32 *rp; int rc; /* estimate risc mem: worst case is one write per page border + @@ -128,7 +128,8 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, unsigned int cpadding) { unsigned int instructions,line,todo,ylen,chroma; - u32 *rp,ri; + __le32 *rp; + u32 ri; struct scatterlist *ysg; struct scatterlist *usg; struct scatterlist *vsg; @@ -244,7 +245,8 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, { int dwords,rc,line,maxy,start,end,skip,nskips; struct btcx_skiplist *skips; - u32 *rp,ri,ra; + __le32 *rp; + u32 ri,ra; u32 addr; /* skip list for window clipping */ diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/video/btcx-risc.c index ce0840ccd594..f42701f82e7f 100644 --- a/drivers/media/video/btcx-risc.c +++ b/drivers/media/video/btcx-risc.c @@ -63,7 +63,7 @@ int btcx_riscmem_alloc(struct pci_dev *pci, struct btcx_riscmem *risc, unsigned int size) { - u32 *cpu; + __le32 *cpu; dma_addr_t dma; if (NULL != risc->cpu && risc->size < size) diff --git a/drivers/media/video/btcx-risc.h b/drivers/media/video/btcx-risc.h index 503e6c6d7b69..861bc8112824 100644 --- a/drivers/media/video/btcx-risc.h +++ b/drivers/media/video/btcx-risc.h @@ -2,8 +2,8 @@ */ struct btcx_riscmem { unsigned int size; - u32 *cpu; - u32 *jmp; + __le32 *cpu; + __le32 *jmp; dma_addr_t dma; }; diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 2a429f9e32cd..03411503457e 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -160,10 +160,17 @@ static int cs5345_probe(struct i2c_client *client, /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id cs5345_id[] = { + { "cs5345", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs5345_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs5345", .driverid = I2C_DRIVERID_CS5345, .command = cs5345_command, .probe = cs5345_probe, + .id_table = cs5345_id, }; diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 2dfd0afc62db..d965af860ab2 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -144,7 +144,8 @@ static int cs53l32a_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - snprintf(client->name, sizeof(client->name) - 1, "cs53l32a"); + if (!id) + strlcpy(client->name, "cs53l32a", sizeof(client->name)); v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); @@ -175,10 +176,17 @@ static int cs53l32a_probe(struct i2c_client *client, return 0; } +static const struct i2c_device_id cs53l32a_id[] = { + { "cs53l32a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs53l32a_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs53l32a", .driverid = I2C_DRIVERID_CS53L32A, .command = cs53l32a_command, .probe = cs53l32a_probe, + .id_table = cs53l32a_id, }; diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index acc4b47f1d1d..9aefdc5ea79a 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -1,14 +1,17 @@ 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 MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_CS5345 - select DVB_S5H1409 + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 66864904c99b..faca43eb940f 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -69,6 +69,58 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } +int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, int no_acfg_mask) +{ + int retval; + u32 saved_reg[8] = {0}; + + if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { + saved_reg[0] = cx18_av_read4(cx, CXADEC_CHIP_CTRL); + saved_reg[1] = cx18_av_read4(cx, CXADEC_AFE_CTRL); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { + saved_reg[2] = cx18_av_read4(cx, CXADEC_PLL_CTRL1); + saved_reg[3] = cx18_av_read4(cx, CXADEC_VID_PLL_FRAC); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_VID) { + saved_reg[4] = cx18_av_read4(cx, CXADEC_HORIZ_TIM_CTRL); + saved_reg[5] = cx18_av_read4(cx, CXADEC_VERT_TIM_CTRL); + saved_reg[6] = cx18_av_read4(cx, CXADEC_SRC_COMB_CFG); + saved_reg[7] = cx18_av_read4(cx, CXADEC_CHROMA_VBIOFF_CFG); + } + + retval = cx18_av_write(cx, addr, value); + + if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { + cx18_av_write4(cx, CXADEC_CHIP_CTRL, saved_reg[0]); + cx18_av_write4(cx, CXADEC_AFE_CTRL, saved_reg[1]); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { + cx18_av_write4(cx, CXADEC_PLL_CTRL1, saved_reg[2]); + cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, saved_reg[3]); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_VID) { + cx18_av_write4(cx, CXADEC_HORIZ_TIM_CTRL, saved_reg[4]); + cx18_av_write4(cx, CXADEC_VERT_TIM_CTRL, saved_reg[5]); + cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, saved_reg[6]); + cx18_av_write4(cx, CXADEC_CHROMA_VBIOFF_CFG, saved_reg[7]); + } + + return retval; +} + +int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned and_mask, + u8 or_value, int no_acfg_mask) +{ + return cx18_av_write_no_acfg(cx, addr, + (cx18_av_read(cx, addr) & and_mask) | + or_value, no_acfg_mask); +} + /* ----------------------------------------------------------------------- */ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, @@ -170,26 +222,30 @@ static void input_change(struct cx18 *cx) /* 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); + cx18_av_write_no_acfg(cx, 0x402, 0, CXADEC_NO_ACFG_ALL); else { - cx18_av_write(cx, 0x402, 0x04); + cx18_av_write_no_acfg(cx, 0x402, 0x04, CXADEC_NO_ACFG_ALL); 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); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0x60, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); 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); } - cx18_av_write(cx, 0x80b, 0x00); } else if (std & V4L2_STD_PAL) { /* Follow tuner change procedure for PAL */ cx18_av_write(cx, 0x808, 0xff); @@ -226,7 +282,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, if ((vid_input & ~0xff0) || luma < CX18_AV_SVIDEO_LUMA1 || - luma > CX18_AV_SVIDEO_LUMA4 || + luma > CX18_AV_SVIDEO_LUMA8 || chroma < CX18_AV_SVIDEO_CHROMA4 || chroma > CX18_AV_SVIDEO_CHROMA8) { CX18_ERR("0x%04x is not a valid video input!\n", @@ -260,7 +316,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, 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); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x6, is_composite ? 0 : 0x02, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); /* 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 */ @@ -316,12 +373,12 @@ static int set_v4lstd(struct cx18 *cx) 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); + cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, 1, CXADEC_NO_ACFG_AFE); /* 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_and_or_no_acfg(cx, 0x400, ~0xf, fmt, CXADEC_NO_ACFG_AFE); + cx18_av_and_or_no_acfg(cx, 0x403, ~0x3, pal_m, CXADEC_NO_ACFG_ALL); cx18_av_vbi_setup(cx); input_change(cx); return 0; @@ -741,8 +798,8 @@ 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, 0x805); - u8 mod_det_stat1 = cx18_av_read(cx, 0x804); + 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); @@ -760,12 +817,12 @@ static void log_audio_status(struct cx18 *cx) case 0x12: p = "dual with SAP"; break; case 0x14: p = "tri with SAP"; break; case 0xfe: p = "forced mode"; break; - default: p = "not defined"; + default: p = "not defined"; break; } CX18_INFO("Detected audio mode: %s\n", p); switch (mod_det_stat1) { - case 0x00: p = "BTSC"; break; + case 0x00: p = "not defined"; break; case 0x01: p = "EIAJ"; break; case 0x02: p = "A2-M"; break; case 0x03: p = "A2-BG"; break; @@ -779,8 +836,13 @@ static void log_audio_status(struct cx18 *cx) 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"; + default: p = "not defined"; break; } CX18_INFO("Detected audio standard: %s\n", p); CX18_INFO("Audio muted: %s\n", @@ -789,22 +851,23 @@ static void log_audio_status(struct cx18 *cx) (download_ctl & 0x10) ? "running" : "stopped"); switch (audio_config >> 4) { - case 0x00: p = "BTSC"; 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 = "FM radio"; break; + 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"; + default: p = "undefined"; break; } CX18_INFO("Configured audio standard: %s\n", p); @@ -815,12 +878,9 @@ static void log_audio_status(struct cx18 *cx) 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 (AB)"; break; - case 0x06: p = "DUAL2 (AC) (FM)"; break; - case 0x07: p = "DUAL3 (BC) (FM)"; break; - case 0x08: p = "DUAL4 (AC) (AM)"; break; - case 0x09: p = "DUAL5 (BC) (AM)"; break; - case 0x0a: p = "SAP"; 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); @@ -835,9 +895,11 @@ static void log_audio_status(struct cx18 *cx) case 0x06: p = "BTSC"; break; case 0x07: p = "EIAJ"; break; case 0x08: p = "A2-M"; break; - case 0x09: p = "FM Radio"; 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"; + default: p = "undefined"; break; } CX18_INFO("Configured audio system: %s\n", p); } @@ -857,22 +919,24 @@ static void log_audio_status(struct cx18 *cx) case 5: p = "language AC"; break; case 6: p = "language BC"; break; case 7: p = "language AB"; break; - default: p = "undefined"; + default: p = "undefined"; break; } CX18_INFO("Preferred audio mode: %s\n", p); if ((audio_config & 0xf) == 0xf) { - switch ((afc0 >> 2) & 0x1) { + 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 & 0x3) { - case 0: p = "BTSC"; break; - case 1: p = "EIAJ"; break; - case 2: p = "A2-M"; break; - default: p = "undefined"; + 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/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 786901d72e9a..c172823ce1d8 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -37,12 +37,16 @@ enum cx18_av_video_input { CX18_AV_COMPOSITE7, CX18_AV_COMPOSITE8, - /* S-Video inputs consist of one luma input (In1-In4) ORed with one + /* S-Video inputs consist of one luma input (In1-In8) 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_LUMA5 = 0x50, + CX18_AV_SVIDEO_LUMA6 = 0x60, + CX18_AV_SVIDEO_LUMA7 = 0x70, + CX18_AV_SVIDEO_LUMA8 = 0x80, CX18_AV_SVIDEO_CHROMA4 = 0x400, CX18_AV_SVIDEO_CHROMA5 = 0x500, CX18_AV_SVIDEO_CHROMA6 = 0x600, @@ -291,14 +295,24 @@ struct cx18_av_state { #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ +/* Flags on what to preserve on write to 0x400-0x403 with cx18_av_.*_no_acfg()*/ +#define CXADEC_NO_ACFG_AFE 0x01 /* Preserve 0x100-0x107 */ +#define CXADEC_NO_ACFG_PLL 0x02 /* Preserve 0x108-0x10f */ +#define CXADEC_NO_ACFG_VID 0x04 /* Preserve 0x470-0x47f */ +#define CXADEC_NO_ACFG_ALL 0x07 + /* ----------------------------------------------------------------------- */ /* 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); +int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, + int no_acfg_mask); 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_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned mask, u8 value, + int no_acfg_mask); int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index f5e3ba1f5354..c26e0ef5b075 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -23,6 +23,7 @@ #include "cx18-driver.h" #include "cx18-cards.h" +#include "cx18-av-core.h" #include "cx18-i2c.h" #include <media/cs5345.h> @@ -47,28 +48,29 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = { static const struct cx18_card cx18_card_hvr1600_esmt = { .type = CX18_CARD_HVR_1600_ESMT, .name = "Hauppauge HVR-1600", - .comment = "DVB & VBI are not yet supported\n", + .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, + .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 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, .ddr = { /* ESMT M13S128324A-5B memory */ .chip_config = 0x003, @@ -80,34 +82,40 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { }, .gpio_init.initial_value = 0x3001, .gpio_init.direction = 0x3001, + .gpio_i2c_slave_reset = { + .active_lo_mask = 0x3001, + .msecs_asserted = 10, + .msecs_recovery = 40, + }, .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 = "DVB & VBI are not yet supported\n", + .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, + .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 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, .ddr = { /* Samsung K4D263238G-VC33 memory */ .chip_config = 0x003, @@ -119,12 +127,17 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { }, .gpio_init.initial_value = 0x3001, .gpio_init.direction = 0x3001, + .gpio_i2c_slave_reset = { + .active_lo_mask = 0x3001, + .msecs_asserted = 10, + .msecs_recovery = 40, + }, .i2c = &cx18_i2c_std, }; /* ------------------------------------------------------------------------- */ -/* Compro VideoMate H900: not working at the moment! */ +/* 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 }, @@ -134,23 +147,24 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = { static const struct cx18_card cx18_card_h900 = { .type = CX18_CARD_COMPRO_H900, .name = "Compro VideoMate H900", - .comment = "Not yet supported!\n", - .v4l2_capabilities = 0, + .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_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, 0 }, + CX18_AV_AUDIO8, 0 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, @@ -163,6 +177,7 @@ static const struct cx18_card cx18_card_h900 = { .tune_lane = 0, .initial_emrs = 0, }, + .xceive_pin = 15, .pci_list = cx18_pci_h900, .i2c = &cx18_i2c_std, }; @@ -179,29 +194,30 @@ static const struct cx18_card_pci_info cx18_pci_mpc718[] = { static const struct cx18_card cx18_card_mpc718 = { .type = CX18_CARD_YUAN_MPC718, .name = "Yuan MPC718", - .comment = "Not yet supported!\n", - .v4l2_capabilities = 0, + .comment = "Some Composite and S-Video inputs are currently working.\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_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, + { CX18_CARD_INPUT_SVIDEO2, 2, + CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, + { CX18_CARD_INPUT_COMPOSITE3, 2, CX18_AV_COMPOSITE3 }, }, .audio_inputs = { - { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, 0 }, - { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, + { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL, 0 }, }, - .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { /* XC3028 tuner */ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, - /* tuner reset */ - .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, .ddr = { /* Probably Samsung K4D263238G-VC33 memory */ .chip_config = 0x003, @@ -211,6 +227,7 @@ static const struct cx18_card cx18_card_mpc718 = { .tune_lane = 0, .initial_emrs = 2, }, + .xceive_pin = 15, .pci_list = cx18_pci_mpc718, .i2c = &cx18_i2c_std, }; diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index bca249bdd337..dc2dd945d4c3 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -36,36 +36,6 @@ #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 @@ -75,16 +45,6 @@ enum cx34180_video_input { #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) @@ -114,8 +74,15 @@ struct cx18_card_pci_info { /* The mask is the set of bits used by the operation */ struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */ - u16 direction; /* DIR setting. Leave to 0 if no init is needed */ - u16 initial_value; + u32 direction; /* DIR setting. Leave to 0 if no init is needed */ + u32 initial_value; +}; + +struct cx18_gpio_i2c_slave_reset { + u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */ + u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */ + int msecs_asserted; /* time period reset must remain asserted */ + int msecs_recovery; /* time after deassert for chips to be ready */ }; struct cx18_card_tuner { @@ -153,7 +120,9 @@ struct cx18_card { struct cx18_card_audio_input radio_input; /* GPIO card-specific settings */ - struct cx18_gpio_init gpio_init; + u8 xceive_pin; /* XCeive tuner GPIO reset pin */ + struct cx18_gpio_init gpio_init; + struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset; struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; struct cx18_card_tuner_i2c *i2c; diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 2bdac5ebbb0d..87cf41021665 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -159,7 +159,7 @@ 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->capturing) > 0) + if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; /* First try to allocate sliced VBI buffers if needed. */ @@ -235,7 +235,7 @@ int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg) 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->capturing), arg, cmd); + int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), arg, cmd); if (err) return err; @@ -295,7 +295,7 @@ int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *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->capturing), arg, cmd); + atomic_read(&cx->ana_capturing), arg, cmd); return -EINVAL; } diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 8f5ed9b4bf83..2b810bb2a4c7 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -164,16 +164,6 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CX18_VERSION); -int cx18_waitq(wait_queue_head_t *waitq) -{ - DEFINE_WAIT(wait); - - prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE); - schedule(); - finish_wait(waitq, &wait); - return signal_pending(current) ? -EINTR : 0; -} - /* Generic utility functions */ int cx18_msleep_timeout(unsigned int msecs, int intr) { @@ -220,13 +210,13 @@ static void cx18_process_eeprom(struct cx18 *cx) /* 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 ... 74099: + case 74000 ... 74999: cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); break; - case 74700 ... 74799: - cx->card = cx18_get_card(CX18_CARD_HVR_1600_SAMSUNG); - break; case 0: CX18_ERR("Invalid EEPROM\n"); return; @@ -548,6 +538,7 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, return 0; } +#ifdef MODULE static u32 cx18_request_module(struct cx18 *cx, u32 hw, const char *name, u32 id) { @@ -560,12 +551,14 @@ static u32 cx18_request_module(struct cx18 *cx, u32 hw, 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); @@ -573,6 +566,7 @@ static void cx18_load_and_init_modules(struct cx18 *cx) #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++) { @@ -613,7 +607,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, } cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); - if (cx == 0) { + if (!cx) { spin_unlock(&cx18_cards_lock); return -ENOMEM; } @@ -676,7 +670,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, cx18_init_power(cx, 1); cx18_init_memory(cx); - cx->scb = (struct cx18_scb *)(cx->enc_mem + SCB_OFFSET); + cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET); cx18_init_scb(cx); cx18_gpio_init(cx); @@ -757,17 +751,6 @@ static int __devinit cx18_probe(struct pci_dev *dev, 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; @@ -794,14 +777,23 @@ static int __devinit cx18_probe(struct pci_dev *dev, are not. */ cx->tuner_std = cx->std; - cx18_init_on_first_open(cx); + 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; + } CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name); return 0; free_streams: - cx18_streams_cleanup(cx); + cx18_streams_cleanup(cx, 1); free_irq: free_irq(cx->dev->irq, (void *)cx); free_i2c: @@ -895,7 +887,7 @@ static void cx18_remove(struct pci_dev *pci_dev) /* Stop all captures */ CX18_DEBUG_INFO("Stopping all streams\n"); - if (atomic_read(&cx->capturing) > 0) + if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); /* Interrupts */ @@ -904,14 +896,13 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); - cx18_streams_cleanup(cx); + cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); free_irq(cx->dev->irq, (void *)cx); - if (cx->dev) - cx18_iounmap(cx); + cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 2ee939193bb7..de14ab59a206 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -358,7 +358,7 @@ struct cx18 { 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_scb __iomem *scb; /* pointer to SCB */ struct cx18_av_state av_state; @@ -380,7 +380,8 @@ struct cx18 { 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 capturing; /* count number of active capture streams */ + 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; @@ -423,6 +424,10 @@ struct cx18 { struct mutex i2c_bus_lock[2]; struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + /* gpio */ + u32 gpio_dir; + u32 gpio_val; + /* v4l2 and User settings */ /* codec settings */ @@ -444,9 +449,6 @@ extern spinlock_t cx18_cards_lock; /* Return non-zero if a signal is pending */ int cx18_msleep_timeout(unsigned int msecs, int intr); -/* Wait on queue, returns -EINTR if interrupted */ -int cx18_waitq(wait_queue_head_t *waitq); - /* Read Hauppauge eeprom */ struct tveeprom; /* forward reference */ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 65efe69d939a..cae38985b131 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -24,25 +24,27 @@ #include "cx18-streams.h" #include "cx18-cards.h" #include "s5h1409.h" - -/* Wait until the MXL500X driver is merged */ -#ifdef HAVE_MXL500X -#include "mxl500x.h" -#endif +#include "mxl5005s.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 -#ifdef HAVE_MXL500X -static struct mxl500x_config hauppauge_hvr1600_tuner = { - .delsys = MXL500x_MODE_ATSC, - .octf = MXL500x_OCTF_CH, - .xtal_freq = 16000000, - .iflo_freq = 5380000, - .ref_freq = 322800000, - .rssi_ena = MXL_RSSI_ENABLE, - .addr = 0xC6 >> 1, +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 = { @@ -55,7 +57,6 @@ static struct s5h1409_config hauppauge_hvr1600_config = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK }; -#endif static int dvb_register(struct cx18_stream *stream); @@ -68,11 +69,21 @@ 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; + int ret; u32 v; CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", feed->pid, feed->index); + + mutex_lock(&cx->serialize_lock); + ret = cx18_init_on_first_open(cx); + mutex_unlock(&cx->serialize_lock); + if (ret) { + CX18_ERR("Failed to initialize firmware starting DVB feed\n"); + return ret; + } + ret = -EINVAL; + switch (cx->card->type) { case CX18_CARD_HVR_1600_ESMT: case CX18_CARD_HVR_1600_SAMSUNG: @@ -100,6 +111,11 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (stream->dvb.feeding++ == 0) { CX18_DEBUG_INFO("Starting Transport DMA\n"); ret = cx18_start_v4l2_encode_stream(stream); + if (ret < 0) { + CX18_DEBUG_INFO( + "Failed to start Transport DMA\n"); + stream->dvb.feeding--; + } } else ret = 0; mutex_unlock(&stream->dvb.feedlock); @@ -252,21 +268,18 @@ static int dvb_register(struct cx18_stream *stream) int ret = 0; switch (cx->card->type) { -/* Wait until the MXL500X driver is merged */ -#ifdef HAVE_MXL500X 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(mxl500x_attach, dvb->fe, - &hauppauge_hvr1600_tuner, - &cx->i2c_adap[0]); + dvb_attach(mxl5005s_attach, dvb->fe, + &cx->i2c_adap[0], + &hauppauge_hvr1600_tuner); ret = 0; } break; -#endif default: /* No Digital Tv Support */ break; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 69303065a294..1e537fe04a23 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -39,7 +39,7 @@ associated VBI streams are also automatically claimed. Possible error returns: -EBUSY if someone else has claimed the stream or 0 on success. */ -int cx18_claim_stream(struct cx18_open_id *id, int type) +static int cx18_claim_stream(struct cx18_open_id *id, int type) { struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[type]; @@ -87,7 +87,7 @@ int cx18_claim_stream(struct cx18_open_id *id, int type) /* This function releases a previously claimed stream. It will take into account associated VBI streams. */ -void cx18_release_stream(struct cx18_stream *s) +static void cx18_release_stream(struct cx18_stream *s) { struct cx18 *cx = s->cx; struct cx18_stream *s_vbi; @@ -318,7 +318,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, size_t tot_written = 0; int single_frame = 0; - if (atomic_read(&cx->capturing) == 0 && s->id == -1) { + 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); @@ -361,7 +361,8 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, 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, + (void __iomem *)&cx->scb->cpu_mdl[buf->id] - + cx->enc_mem, 1, buf->id, s->buf_size); } else cx18_enqueue(s, buf, &s->q_io); @@ -581,7 +582,7 @@ int cx18_v4l2_close(struct inode *inode, struct file *filp) 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->capturing) > 0) { + 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 | @@ -627,7 +628,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) } if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { - if (atomic_read(&cx->capturing) > 0) { + if (atomic_read(&cx->ana_capturing) > 0) { /* switching to radio while capture is in progress is not polite */ cx18_release_stream(s); @@ -662,6 +663,8 @@ int cx18_v4l2_open(struct inode *inode, struct file *filp) 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]; @@ -692,7 +695,7 @@ int cx18_v4l2_open(struct inode *inode, struct file *filp) void cx18_mute(struct cx18 *cx) { - if (atomic_read(&cx->capturing)) + 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"); @@ -700,7 +703,7 @@ void cx18_mute(struct cx18 *cx) void cx18_unmute(struct cx18 *cx) { - if (atomic_read(&cx->capturing)) { + 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); diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h index 16cdafbd24c5..46da0282fc7d 100644 --- a/drivers/media/video/cx18/cx18-fileops.h +++ b/drivers/media/video/cx18/cx18-fileops.h @@ -34,12 +34,3 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end); void cx18_mute(struct cx18 *cx); void cx18_unmute(struct cx18 *cx); -/* Utilities */ - -/* Try to claim a stream for the filehandle. Return 0 on success, - -EBUSY if stream already claimed. Once a stream is claimed, it - remains claimed until the associated filehandle is closed. */ -int cx18_claim_stream(struct cx18_open_id *id, int type); - -/* Release a previously claimed stream. */ -void cx18_release_stream(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index 19253e6b8673..b302833f6f9d 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -44,31 +44,81 @@ * 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_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), + CX18_REG_GPIO_OUT2); +} + +void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) +{ + const struct cx18_gpio_i2c_slave_reset *p; + + p = &cx->card->gpio_i2c_slave_reset; + + if ((p->active_lo_mask | p->active_hi_mask) == 0) + return; + + /* Assuming that the masks are a subset of the bits in gpio_dir */ + + /* Assert */ + cx->gpio_val = + (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); + gpio_write(cx); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); + + /* Deassert */ + cx->gpio_val = + (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); + gpio_write(cx); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); +} + void cx18_gpio_init(struct cx18 *cx) { - if (cx->card->gpio_init.direction == 0) + cx->gpio_dir = cx->card->gpio_init.direction; + cx->gpio_val = cx->card->gpio_init.initial_value; + + if (cx->card->tuners[0].tuner == TUNER_XC2028) { + 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 out: %08x\n", - read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_OUT1)); + 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)); - /* init output data then direction */ - write_reg(cx->card->gpio_init.direction << 16, CX18_REG_GPIO_DIR1); - write_reg(0, CX18_REG_GPIO_DIR2); - write_reg((cx->card->gpio_init.direction << 16) | - cx->card->gpio_init.initial_value, CX18_REG_GPIO_OUT1); - write_reg(0, 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 *cx = algo->data; -/* int curdir, curout;*/ + 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/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index 41bac8856b50..525c328f748a 100644 --- a/drivers/media/video/cx18/cx18-gpio.h +++ b/drivers/media/video/cx18/cx18-gpio.h @@ -21,4 +21,5 @@ */ void cx18_gpio_init(struct cx18 *cx); +void cx18_reset_i2c_slaves_gpio(struct cx18 *cx); int cx18_reset_tuner_gpio(void *dev, int cmd, int value); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 18c88d1e4833..680bc4e35b79 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -25,6 +25,7 @@ #include "cx18-cards.h" #include "cx18-gpio.h" #include "cx18-av-core.h" +#include "cx18-i2c.h" #include <media/ir-kbd-i2c.h> @@ -73,7 +74,7 @@ static const u8 hw_bus[] = { }; /* This array should match the CX18_HW_ defines */ -static const char * const hw_drivernames[] = { +static const char * const hw_devicenames[] = { "tuner", "tveeprom", "cs5345", @@ -94,8 +95,7 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) id = hw_driverids[idx]; bus = hw_bus[idx]; memset(&info, 0, sizeof(info)); - strlcpy(info.driver_name, hw_drivernames[idx], - sizeof(info.driver_name)); + strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); info.addr = hw_addrs[idx]; for (i = 0; i < I2C_CLIENTS_MAX; i++) if (cx->i2c_clients[i] == NULL) @@ -278,7 +278,7 @@ static const char *cx18_i2c_id_name(u32 id) for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) if (hw_driverids[i] == id) - return hw_drivernames[i]; + return hw_devicenames[i]; return "unknown device"; } @@ -289,7 +289,7 @@ static const char *cx18_i2c_hw_name(u32 hw) for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) if (1 << i == hw) - return hw_drivernames[i]; + return hw_devicenames[i]; return "unknown device"; } @@ -405,6 +405,8 @@ int init_cx18_i2c(struct cx18 *cx) cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); + cx18_reset_i2c_slaves_gpio(cx); + return i2c_bit_add_bus(&cx->i2c_adap[0]) || i2c_bit_add_bus(&cx->i2c_adap[1]); } diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index dbdcb86ec5aa..4151f1e5493f 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -247,7 +247,7 @@ static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype, if (!set_fmt || (cx->params.width == w && cx->params.height == h)) return 0; - if (atomic_read(&cx->capturing) > 0) + if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; cx->params.width = w; @@ -264,7 +264,7 @@ static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype, 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->capturing) > 0) + atomic_read(&cx->ana_capturing) > 0) return -EBUSY; if (set_fmt) { cx->vbi.sliced_in->service_set = 0; @@ -293,7 +293,7 @@ static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype, return 0; if (set == 0) return -EINVAL; - if (atomic_read(&cx->capturing) > 0 && cx->vbi.sliced_in->service_set == 0) + 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)); @@ -581,7 +581,7 @@ int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg break; if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || - atomic_read(&cx->capturing) > 0) { + atomic_read(&cx->ana_capturing) > 0) { /* Switching standard would turn off the radio or mess with already running streams, prevent that by returning EBUSY. */ @@ -677,7 +677,7 @@ int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg enc->flags = 0; if (try) return 0; - if (!atomic_read(&cx->capturing)) + if (!atomic_read(&cx->ana_capturing)) return -EPERM; if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) return 0; @@ -689,7 +689,7 @@ int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg enc->flags = 0; if (try) return 0; - if (!atomic_read(&cx->capturing)) + if (!atomic_read(&cx->ana_capturing)) return -EPERM; if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) return 0; diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c index 6e14f8bda559..25114a5cbd57 100644 --- a/drivers/media/video/cx18/cx18-irq.c +++ b/drivers/media/video/cx18/cx18-irq.c @@ -75,7 +75,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) 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, + (void __iomem *)&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); @@ -161,13 +161,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) */ if (sw2) { - if (sw2 & (cx->scb->cpu2hpu_irq_ack | cx->scb->cpu2epu_irq_ack)) + if (sw2 & (readl(&cx->scb->cpu2hpu_irq_ack) | + readl(&cx->scb->cpu2epu_irq_ack))) wake_up(&cx->mb_cpu_waitq); - if (sw2 & (cx->scb->apu2hpu_irq_ack | cx->scb->apu2epu_irq_ack)) + if (sw2 & (readl(&cx->scb->apu2hpu_irq_ack) | + readl(&cx->scb->apu2epu_irq_ack))) wake_up(&cx->mb_apu_waitq); - if (sw2 & cx->scb->epu2hpu_irq_ack) + if (sw2 & readl(&cx->scb->epu2hpu_irq_ack)) wake_up(&cx->mb_epu_waitq); - if (sw2 & cx->scb->hpu2epu_irq_ack) + if (sw2 & readl(&cx->scb->hpu2epu_irq_ack)) wake_up(&cx->mb_hpu_waitq); } diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 0c5f328bca54..2a5ccef9185b 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -94,10 +94,10 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -static struct cx18_mailbox *cx18_mb_is_complete(struct cx18 *cx, int rpu, +static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, u32 *state, u32 *irq, u32 *req) { - struct cx18_mailbox *mb = NULL; + struct cx18_mailbox __iomem *mb = NULL; int wait_count = 0; u32 ack; @@ -142,7 +142,7 @@ static struct cx18_mailbox *cx18_mb_is_complete(struct cx18 *cx, int rpu, 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; + struct cx18_mailbox __iomem *ack_mb; u32 ack_irq; u8 rpu = CPU; @@ -182,7 +182,7 @@ 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; + struct cx18_mailbox __iomem *mb; wait_queue_head_t *waitq; int timeout = 100; int cnt = 0; diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 65af1bb507ca..6990b77c6200 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -26,17 +26,6 @@ #include "cx18-queue.h" #include "cx18-scb.h" -int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf, - const char __user *src, int copybytes) -{ - if (s->buf_size - buf->bytesused < copybytes) - copybytes = s->buf_size - buf->bytesused; - if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) - return -EFAULT; - buf->bytesused += copybytes; - return copybytes; -} - void cx18_buf_swap(struct cx18_buffer *buf) { int i; @@ -159,8 +148,9 @@ static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from, -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. */ -int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from, - struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes) +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; @@ -239,12 +229,12 @@ int cx18_stream_alloc(struct cx18_stream *s) /* 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); + 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); + buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); if (buf->buf == NULL) { kfree(buf); break; diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index f86c8a6fa6e7..91423b9863a4 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -39,8 +39,6 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, s->buf_size, s->dma); } -int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf, - const char __user *src, int copybytes); void cx18_buf_swap(struct cx18_buffer *buf); /* cx18_queue utility functions */ @@ -48,8 +46,6 @@ 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); -int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from, - struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes); struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index afb141b2027a..1b921a336092 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -36,12 +36,13 @@ #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, - .release = cx18_v4l2_close, - .poll = cx18_v4l2_enc_poll, + .owner = THIS_MODULE, + .read = cx18_v4l2_read, + .open = cx18_v4l2_open, + .ioctl = cx18_v4l2_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .release = cx18_v4l2_close, + .poll = cx18_v4l2_enc_poll, }; /* offset from 0 to register ts v4l2 minors on */ @@ -218,7 +219,7 @@ int cx18_streams_setup(struct cx18 *cx) return 0; /* One or more streams could not be initialized. Clean 'em all up. */ - cx18_streams_cleanup(cx); + cx18_streams_cleanup(cx, 0); return -ENOMEM; } @@ -296,12 +297,12 @@ int cx18_streams_register(struct cx18 *cx) return 0; /* One or more streams could not be initialized. Clean 'em all up. */ - cx18_streams_cleanup(cx); + cx18_streams_cleanup(cx, 1); return -ENOMEM; } /* Unregister v4l2 devices */ -void cx18_streams_cleanup(struct cx18 *cx) +void cx18_streams_cleanup(struct cx18 *cx, int unregister) { struct video_device *vdev; int type; @@ -319,8 +320,11 @@ void cx18_streams_cleanup(struct cx18 *cx) cx18_stream_free(&cx->streams[type]); - /* Unregister device */ - video_unregister_device(vdev); + /* Unregister or release device */ + if (unregister) + video_unregister_device(vdev); + else + video_device_release(vdev); } } @@ -440,7 +444,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) s->handle = data[0]; cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); - if (atomic_read(&cx->capturing) == 0 && !ts) { + 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); @@ -463,14 +467,14 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx2341x_update(cx, cx18_api_func, NULL, &cx->params); } - if (atomic_read(&cx->capturing) == 0) { + 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); + (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, + (void __iomem *)&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); @@ -478,8 +482,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) 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); + (void __iomem *)&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)) { @@ -489,7 +493,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) } /* you're live! sit back and await interrupts :) */ - atomic_inc(&cx->capturing); + if (!ts) + atomic_inc(&cx->ana_capturing); + atomic_inc(&cx->tot_capturing); return 0; } @@ -520,7 +526,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) CX18_DEBUG_INFO("Stop Capture\n"); - if (atomic_read(&cx->capturing) == 0) + if (atomic_read(&cx->tot_capturing) == 0) return 0; if (s->type == CX18_ENC_STREAM_TYPE_MPG) @@ -534,7 +540,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); } - atomic_dec(&cx->capturing); + 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); @@ -542,7 +550,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = 0xffffffff; - if (atomic_read(&cx->capturing) > 0) + if (atomic_read(&cx->tot_capturing) > 0) return 0; write_reg(5, CX18_DSP0_INTERRUPT_MASK); diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 8c7ba7d2fa79..f327e947b24f 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -24,7 +24,7 @@ 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); +void cx18_streams_cleanup(struct cx18 *cx, int unregister); /* Capture related */ int cx18_start_v4l2_encode_stream(struct cx18_stream *s); diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index cadf936c3673..7bf14c9a15c7 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -1,18 +1,20 @@ 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 - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR select VIDEOBUF_DVB 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 MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 6ebf58724a01..20e05f230546 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -200,6 +200,10 @@ struct cx23885_subid cx23885_subids[] = { .card = CX23885_BOARD_HAUPPAUGE_HVR1200, }, { .subvendor = 0x0070, + .subdevice = 0x71d3, + .card = CX23885_BOARD_HAUPPAUGE_HVR1200, + }, { + .subvendor = 0x0070, .subdevice = 0x8101, .card = CX23885_BOARD_HAUPPAUGE_HVR1700, }, { @@ -245,6 +249,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 */ @@ -263,8 +294,11 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) 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, Retail, No IR, half height, + /* WinTV-HVR1700 (PCIe, OEM, No IR, full height) * DVB-T and MPEG2 HW Encoder */ break; default: diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index f24abcd06dea..c4cc2f3b8876 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -823,7 +823,7 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) iounmap(dev->lmmio); } -static u32* cx23885_risc_field(u32 *rp, struct scatterlist *sglist, +static __le32* cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, unsigned int lines) @@ -883,7 +883,7 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, unsigned int padding, unsigned int lines) { u32 instructions, fields; - u32 *rp; + __le32 *rp; int rc; fields = 0; @@ -924,7 +924,7 @@ static int cx23885_risc_databuffer(struct pci_dev *pci, unsigned int lines) { u32 instructions; - u32 *rp; + __le32 *rp; int rc; /* estimate risc mem: worst case is one write per page border + @@ -951,7 +951,7 @@ static int cx23885_risc_databuffer(struct pci_dev *pci, int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value) { - u32 *rp; + __le32 *rp; int rc; if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0) diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index f05649727b60..022aa391937a 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -37,7 +37,6 @@ #include "lgdt330x.h" #include "xc5000.h" #include "tda10048.h" -#include "dvb-pll.h" #include "tuner-xc2028.h" #include "tuner-simple.h" #include "dib7000p.h" @@ -385,12 +384,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]; diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig index 7cf29a03ed63..448f4cd0ce34 100644 --- a/drivers/media/video/cx25840/Kconfig +++ b/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/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 88823810497c..1da6f134888d 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -433,7 +433,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp int chroma = vid_input & 0xf00; if ((vid_input & ~0xff0) || - luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 || + luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); @@ -1284,10 +1284,17 @@ static int cx25840_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id cx25840_id[] = { + { "cx25840", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cx25840_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cx25840", .driverid = I2C_DRIVERID_CX25840, .command = cx25840_command, .probe = cx25840_probe, .remove = cx25840_remove, + .id_table = cx25840_id, }; diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index b0d7d6a7a4cc..10e20d8196dc 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -2,10 +2,9 @@ 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 MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO @@ -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 diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index e976fc6bef7c..80c8883e54b5 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -332,6 +332,12 @@ static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; int err; + if (!chip) { + printk(KERN_ERR "BUG: cx88 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) goto _error; diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index aeba26dc0a37..fa6d398e97b9 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1493,10 +1493,16 @@ static const struct cx88_board cx88_boards[] = { }, }, [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { - .name = "PowerColor Real Angel 330", + .name = "PowerColor RA330", /* Long names may confuse LIRC. */ .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, .input = { { + .type = CX88_VMUX_DEBUG, + .vmux = 3, /* Due to the way the cx88 driver is written, */ + .gpio0 = 0x00ff, /* there is no way to deactivate audio pass- */ + .gpio1 = 0xf39d, /* through without this entry. Furthermore, if */ + .gpio3 = 0x0000, /* the TV mux entry is first, you get audio */ + }, { /* from the tuner on boot for a little while. */ .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00ff, @@ -2424,8 +2430,9 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) switch (core->boardnr) { case CX88_BOARD_POWERCOLOR_REAL_ANGEL: - /* Doesn't work with firmware version 2.7 */ - ctl->fname = "xc3028-v25.fw"; + /* Now works with firmware version 2.7 */ + if (core->i2c_algo.udelay < 16) + core->i2c_algo.udelay = 16; break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: ctl->scode_table = XC3028_FE_ZARLINK456; diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index c4d1aff1fdb4..60eeda3057e9 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -70,7 +70,7 @@ static DEFINE_MUTEX(devlist); /* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be generated _after_ lpi lines are transferred. */ -static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist, +static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, unsigned int lines, unsigned int lpi) @@ -130,7 +130,7 @@ int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, unsigned int bpl, unsigned int padding, unsigned int lines) { u32 instructions,fields; - u32 *rp; + __le32 *rp; int rc; fields = 0; @@ -168,7 +168,7 @@ int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, unsigned int lines, unsigned int lpi) { u32 instructions; - u32 *rp; + __le32 *rp; int rc; /* estimate risc mem: worst case is one write per page border + @@ -193,7 +193,7 @@ int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, u32 reg, u32 mask, u32 value) { - u32 *rp; + __le32 *rp; int rc; if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0) diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 1c7fe6862a60..d96173ff1dba 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -509,9 +509,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; } @@ -523,20 +520,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: @@ -545,11 +545,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: @@ -559,29 +560,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: @@ -589,28 +593,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: @@ -618,10 +625,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: @@ -630,32 +638,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 @@ -668,19 +679,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); @@ -690,139 +700,137 @@ 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, }; @@ -841,50 +849,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, - &dev->core->i2c_adap); + &core->i2c_adap); if (dev->dvb.frontend != NULL) { /* tuner_config.video_dev must point to * i2c_adap.algo_data */ - dvico_fusionhdtv7_tuner_config.priv = - dev->core->i2c_adap.algo_data; - dvb_attach(xc5000_attach, dev->dvb.frontend, - &dev->core->i2c_adap, - &dvico_fusionhdtv7_tuner_config); + 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; } @@ -892,11 +900,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/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index c7c2896bbd8b..16a5af30e9d1 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -1,7 +1,7 @@ config VIDEO_EM28XX tristate "Empia EM28xx USB video capture support" depends on VIDEO_DEV && I2C && INPUT - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR select VIDEOBUF_VMALLOC @@ -35,7 +35,6 @@ config VIDEO_EM28XX_DVB select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB - select FW_LOADER ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 92b2a6db4fdc..3c006103c1eb 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -268,6 +268,12 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); + if (!dev) { + printk(KERN_ERR "BUG: em28xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + /* Sets volume, mute, etc */ dev->mute = 0; @@ -415,6 +421,12 @@ static int em28xx_audio_init(struct em28xx *dev) static int devnr; int ret, err; + if (dev->has_audio_class) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module */ + return 0; + } + printk(KERN_INFO "em28xx-audio.c: probing for em28x1 " "non standard usbaudio\n"); printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " @@ -458,6 +470,12 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev == NULL) return 0; + if (dev->has_audio_class) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module */ + return 0; + } + if (dev->adev) { snd_card_free(dev->adev->sndcard); kfree(dev->adev); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 50ccf3771204..8cbda43727c3 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -157,6 +157,7 @@ struct em28xx_board em28xx_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -420,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 }, @@ -518,6 +525,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { switch (rc) { + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; case CHIP_ID_EM2883: em28xx_info("chip ID is em2882/em2883\n"); dev->wait_after_write = 0; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 7df81575b7f2..0b2333ee07f8 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -251,7 +251,6 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) 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 -EINVAL; } @@ -383,6 +382,11 @@ static int dvb_init(struct em28xx *dev) int result = 0; struct em28xx_dvb *dvb; + if (!dev->has_dvb) { + /* This device does not support the extension */ + return 0; + } + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { @@ -445,6 +449,11 @@ out_free: static int dvb_fini(struct em28xx *dev) { + if (!dev->has_dvb) { + /* This device does not support the extension */ + return 0; + } + if (dev->dvb) { unregister_dvb(dev->dvb); dev->dvb = NULL; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 9058bed07953..fac1ab23f621 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -84,5 +84,6 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2860 = 34, CHIP_ID_EM2883 = 36, }; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 8996175cc950..285bc62bbe46 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1166,13 +1166,13 @@ static int vidioc_g_register(struct file *file, void *priv, reg->val = ret; } else { - u64 val = 0; + __le64 val = 0; ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, reg->reg, (char *)&val, 2); if (ret < 0) return ret; - reg->val = cpu_to_le64((__u64)val); + reg->val = le64_to_cpu(val); } return 0; @@ -1183,9 +1183,9 @@ static int vidioc_s_register(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - u64 buf; + __le64 buf; - buf = le64_to_cpu((__u64)reg->val); + buf = cpu_to_le64(reg->val); return em28xx_write_regs(dev, reg->reg, (char *)&buf, em28xx_reg_len(reg->reg)); @@ -1848,32 +1848,28 @@ static DEFINE_MUTEX(em28xx_extension_devlist_lock); int em28xx_register_extension(struct em28xx_ops *ops) { - struct em28xx *h, *dev = NULL; - - list_for_each_entry(h, &em28xx_devlist, devlist) - dev = h; + struct em28xx *dev = NULL; mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); - if (dev) - ops->init(dev); - + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->init(dev); + } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); mutex_unlock(&em28xx_extension_devlist_lock); - return 0; } EXPORT_SYMBOL(em28xx_register_extension); void em28xx_unregister_extension(struct em28xx_ops *ops) { - struct em28xx *h, *dev = NULL; - - list_for_each_entry(h, &em28xx_devlist, devlist) - dev = h; + struct em28xx *dev = NULL; - if (dev) - ops->fini(dev); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } mutex_lock(&em28xx_extension_devlist_lock); printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 5e749c528a62..15d037ae25c5 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -34,7 +34,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/page-flags.h> -#include <linux/byteorder/generic.h> +#include <asm/byteorder.h> #include <asm/page.h> #include <asm/uaccess.h> diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig index eec115bf9517..5d7ee8fcdd50 100644 --- a/drivers/media/video/ivtv/Kconfig +++ b/drivers/media/video/ivtv/Kconfig @@ -1,10 +1,12 @@ 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 - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_CX25840 diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index 8c02fa661591..c7e449f6397b 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/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/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index ed020f722b05..797e636771da 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -853,6 +853,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) { @@ -865,12 +866,14 @@ 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_MEDIA_TUNER hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER); @@ -911,6 +914,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++) { @@ -1228,7 +1232,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, return 0; free_streams: - ivtv_streams_cleanup(itv); + ivtv_streams_cleanup(itv, 1); free_irq: free_irq(itv->dev->irq, (void *)itv); free_i2c: @@ -1373,7 +1377,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/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index ba06e813c58c..9d23b1efd36d 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -259,6 +259,12 @@ struct ivtv_mailbox_data { /* Scatter-Gather array element, used in DMA transfers */ struct ivtv_sg_element { + __le32 src; + __le32 dst; + __le32 size; +}; + +struct ivtv_sg_host_element { u32 src; u32 dst; u32 size; @@ -349,8 +355,8 @@ struct ivtv_stream { u16 dma_xfer_cnt; /* Base Dev SG Array for cx23415/6 */ - struct ivtv_sg_element *sg_pending; - struct ivtv_sg_element *sg_processing; + struct ivtv_sg_host_element *sg_pending; + struct ivtv_sg_host_element *sg_processing; struct ivtv_sg_element *sg_dma; dma_addr_t sg_handle; int sg_pending_size; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 2b74b0ab1477..db813e071ce6 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -587,7 +587,7 @@ retry: since we may get here before the stream has been fully set-up */ if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) { while (count >= itv->dma_data_req_size) { - if (!ivtv_yuv_udma_stream_frame (itv, (void *)user_buf)) { + if (!ivtv_yuv_udma_stream_frame (itv, (void __user *)user_buf)) { bytes_written += itv->dma_data_req_size; user_buf += itv->dma_data_req_size; count -= itv->dma_data_req_size; @@ -987,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/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 771adf47e944..32129f3ea836 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -136,7 +136,7 @@ static const u8 hw_addrs[] = { }; /* This array should match the IVTV_HW_ defines */ -static const char * const hw_drivernames[] = { +static const char * const hw_devicenames[] = { "cx25840", "saa7115", "saa7127", @@ -145,7 +145,7 @@ static const char * const hw_drivernames[] = { "wm8775", "cs53l32a", "tveeprom", - "saa7115", + "saa7114", "upd64031a", "upd64083", "saa717x", @@ -167,8 +167,7 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) return -1; id = hw_driverids[idx]; memset(&info, 0, sizeof(info)); - strlcpy(info.driver_name, hw_drivernames[idx], - sizeof(info.driver_name)); + strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); info.addr = hw_addrs[idx]; for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {} @@ -657,7 +656,7 @@ static const char *ivtv_i2c_id_name(u32 id) for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) if (hw_driverids[i] == id) - return hw_drivernames[i]; + return hw_devicenames[i]; return "unknown device"; } @@ -668,7 +667,7 @@ static const char *ivtv_i2c_hw_name(u32 hw) for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) if (1 << i == hw) - return hw_drivernames[i]; + return hw_devicenames[i]; return "unknown device"; } @@ -770,7 +769,7 @@ int init_ivtv_i2c(struct ivtv *itv) * same size and GPIO must be the last entry. */ if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_drivernames) != ARRAY_SIZE(hw_addrs) || + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { IVTV_ERR("Mismatched I2C hardware arrays\n"); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index d508b5d0538c..26cc0f6699fd 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/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; @@ -466,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; } @@ -481,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: @@ -640,9 +640,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; diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h index a03351b6853d..4e67f0ed1fc0 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.h +++ b/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/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index d8ba3a4a8761..fba150a6cd23 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -231,14 +231,14 @@ static void dma_post(struct ivtv_stream *s) struct ivtv_buffer *buf = NULL; struct list_head *p; u32 offset; - u32 *u32buf; + __le32 *u32buf; int x = 0; IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", s->name, s->dma_offset); list_for_each(p, &s->q_dma.list) { buf = list_entry(p, struct ivtv_buffer, list); - u32buf = (u32 *)buf->buf; + u32buf = (__le32 *)buf->buf; /* Sync Buffer */ ivtv_buf_sync_for_cpu(s, buf); @@ -444,7 +444,7 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) } s->dma_xfer_cnt++; - memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_element) * s->sg_pending_size); + memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size); s->sg_processing_size = s->sg_pending_size; s->sg_pending_size = 0; s->sg_processed = 0; @@ -473,7 +473,7 @@ static void ivtv_dma_dec_start(struct ivtv_stream *s) if (s->q_predma.bytesused) ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); s->dma_xfer_cnt++; - memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_element) * s->sg_pending_size); + memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size); s->sg_processing_size = s->sg_pending_size; s->sg_pending_size = 0; s->sg_processed = 0; diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c index 3e1deec67a5e..71bd13e22e2e 100644 --- a/drivers/media/video/ivtv/ivtv-queue.c +++ b/drivers/media/video/ivtv/ivtv-queue.c @@ -193,7 +193,7 @@ void ivtv_flush_queues(struct ivtv_stream *s) int ivtv_stream_alloc(struct ivtv_stream *s) { struct ivtv *itv = s->itv; - int SGsize = sizeof(struct ivtv_sg_element) * s->buffers; + int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers; int i; if (s->buffers == 0) @@ -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/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 4ab8d36831ba..c854285a4371 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -44,23 +44,25 @@ #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, + .compat_ioctl = v4l_compat_ioctl32, + .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, + .compat_ioctl = v4l_compat_ioctl32, + .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 +246,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 +306,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 +324,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/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h index 3d76a415fbd8..a653a5136417 100644 --- a/drivers/media/video/ivtv/ivtv-streams.h +++ b/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/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index c151bcf5519a..71798f0da27f 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/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/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h index 02c5ab071d1b..442f43f11b73 100644 --- a/drivers/media/video/ivtv/ivtv-version.h +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -22,8 +22,8 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 -#define IVTV_DRIVER_VERSION_MINOR 2 -#define IVTV_DRIVER_VERSION_PATCHLEVEL 1 +#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) #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c index 62f70bd5e3cb..3092ff1d00a0 100644 --- a/drivers/media/video/ivtv/ivtv-yuv.c +++ b/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 { @@ -1116,7 +1116,7 @@ void ivtv_yuv_setup_stream_frame(struct ivtv *itv) } /* Attempt to dma a frame from a user buffer */ -int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src) +int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src) { struct yuv_playback_info *yi = &itv->yuv_info; struct ivtv_dma_frame dma_args; diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h index 2fe5f1250762..ca5173fbf006 100644 --- a/drivers/media/video/ivtv/ivtv-yuv.h +++ b/drivers/media/video/ivtv/ivtv-yuv.h @@ -35,7 +35,7 @@ extern const u32 yuv_offset[IVTV_YUV_BUFFERS]; int ivtv_yuv_filter_check(struct ivtv *itv); void ivtv_yuv_setup_stream_frame(struct ivtv *itv); -int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void *src); +int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src); void ivtv_yuv_frame_complete(struct ivtv *itv); int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); void ivtv_yuv_close(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index df789f683e63..73be154f7f05 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -948,7 +948,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 +1057,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/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 5b9dfa2c51b4..8e0160d275ca 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -135,8 +135,6 @@ static int m52790_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - snprintf(client->name, sizeof(client->name) - 1, "m52790"); - v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); @@ -159,11 +157,18 @@ static int m52790_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id m52790_id[] = { + { "m52790", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, m52790_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "m52790", .driverid = I2C_DRIVERID_M52790, .command = m52790_command, .probe = m52790_probe, .remove = m52790_remove, + .id_table = m52790_id, }; diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index e6273162e123..310dbaba55ff 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -815,7 +815,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) int msp_product, msp_prod_hi, msp_prod_lo; int msp_rom; - snprintf(client->name, sizeof(client->name) - 1, "msp3400"); + if (!id) + strlcpy(client->name, "msp3400", sizeof(client->name)); if (msp_reset(client) == -1) { v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); @@ -864,9 +865,6 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) msp_revision = (state->rev1 & 0x0f) + '@'; msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; msp_rom = state->rev2 & 0x1f; - snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d", - msp_family, msp_product, - msp_revision, msp_hard, msp_rom); /* Rev B=2, C=3, D=4, G=7 */ state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@'; @@ -931,7 +929,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* hello world :-) */ - v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, + v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", + msp_family, msp_product, + msp_revision, msp_hard, msp_rom, client->addr << 1, client->adapter->name); v4l_info(client, "%s ", client->name); if (state->has_nicam && state->has_radio) @@ -987,6 +987,12 @@ static int msp_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id msp_id[] = { + { "msp3400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, msp_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "msp3400", .driverid = I2C_DRIVERID_MSP3400, @@ -995,6 +1001,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .remove = msp_remove, .suspend = msp_suspend, .resume = msp_resume, + .id_table = msp_id, }; diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 179e47049a45..ee43499544c1 100644 --- a/drivers/media/video/mt9m001.c +++ b/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() */ diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index d1391ac55096..1658fe590392 100644 --- a/drivers/media/video/mt9v022.c +++ b/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; diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index 9620c67fae77..4482b2c72ced 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -1,8 +1,10 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" 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 MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_SAA711X diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 7cc8e9b19fb7..5ec5bb9a94d2 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -1019,12 +1019,12 @@ static int pxa_camera_probe(struct platform_device *pdev) struct pxa_camera_dev *pcdev; struct resource *res; void __iomem *base; - unsigned int irq; + int irq; int err = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!res || !irq) { + if (!res || irq < 0) { err = -ENODEV; goto exit; } diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index e684108637ad..435c083cc542 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1456,14 +1456,13 @@ static int saa7115_probe(struct i2c_client *client, struct saa711x_state *state; int i; char name[17]; - u8 chip_id; + char chip_id; + int autodetect = !id || id->driver_data == 1; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - snprintf(client->name, sizeof(client->name) - 1, "saa7115"); - for (i = 0; i < 0x0f; i++) { saa711x_write(client, 0, i); name[i] = (saa711x_read(client, 0) & 0x0f) + '0'; @@ -1472,8 +1471,7 @@ static int saa7115_probe(struct i2c_client *client, } name[i] = '\0'; - saa711x_write(client, 0, 5); - chip_id = saa711x_read(client, 0) & 0x0f; + chip_id = name[5]; /* Check whether this chip is part of the saa711x series */ if (memcmp(name, "1f711", 5)) { @@ -1482,8 +1480,14 @@ static int saa7115_probe(struct i2c_client *client, return -ENODEV; } - snprintf(client->name, sizeof(client->name) - 1, "saa711%d",chip_id); - v4l_info(client, "saa711%d found (%s) @ 0x%x (%s)\n", chip_id, name, client->addr << 1, client->adapter->name); + /* Safety check */ + if (!autodetect && id->name[6] != chip_id) { + v4l_warn(client, "found saa711%c while %s was expected\n", + chip_id, id->name); + } + snprintf(client->name, sizeof(client->name), "saa711%c", chip_id); + v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name, + client->addr << 1, client->adapter->name); state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); i2c_set_clientdata(client, state); @@ -1499,19 +1503,19 @@ static int saa7115_probe(struct i2c_client *client, state->hue = 0; state->sat = 64; switch (chip_id) { - case 1: + case '1': state->ident = V4L2_IDENT_SAA7111; break; - case 3: + case '3': state->ident = V4L2_IDENT_SAA7113; break; - case 4: + case '4': state->ident = V4L2_IDENT_SAA7114; break; - case 5: + case '5': state->ident = V4L2_IDENT_SAA7115; break; - case 8: + case '8': state->ident = V4L2_IDENT_SAA7118; break; default: @@ -1553,6 +1557,17 @@ static int saa7115_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id saa7115_id[] = { + { "saa711x", 1 }, /* autodetect */ + { "saa7111", 0 }, + { "saa7113", 0 }, + { "saa7114", 0 }, + { "saa7115", 0 }, + { "saa7118", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7115_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7115", .driverid = I2C_DRIVERID_SAA711X, @@ -1560,5 +1575,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .probe = saa7115_probe, .remove = saa7115_remove, .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, + .id_table = saa7115_id, }; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index e750cd65c1c3..79d11a658bdf 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -672,8 +672,6 @@ static int saa7127_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - snprintf(client->name, sizeof(client->name) - 1, "saa7127"); - v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", client->addr << 1); @@ -741,11 +739,18 @@ static int saa7127_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static struct i2c_device_id saa7127_id[] = { + { "saa7127", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7127_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7127", .driverid = I2C_DRIVERID_SAA7127, .command = saa7127_command, .probe = saa7127_probe, .remove = saa7127_remove, + .id_table = saa7127_id, }; diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 40e4c3bd2cb9..83f076abce35 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -3,7 +3,7 @@ config VIDEO_SAA7134 depends on VIDEO_DEV && PCI && I2C && INPUT select VIDEOBUF_DMA_SG select VIDEO_IR - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 ---help--- @@ -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 diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index ba3082422a01..f118de6e3672 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -613,9 +613,15 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) struct snd_pcm_runtime *runtime = substream->runtime; snd_card_saa7134_pcm_t *pcm; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); - struct saa7134_dev *dev = saa7134->dev; + struct saa7134_dev *dev; int amux, err; + if (!saa7134) { + printk(KERN_ERR "BUG: saa7134 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + dev = saa7134->dev; mutex_lock(&dev->dmasound.lock); dev->dmasound.read_count = 0; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index b111903aa322..2618cfa592e7 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4114,11 +4114,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - /* - TODO: .mpeg = SAA7134_MPEG_DVB, - */ - .inputs = {{ .name = name_tv, .vmux = 1, @@ -4157,7 +4153,7 @@ struct saa7134_board saa7134_boards[] = { } }, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, }, }, [SAA7134_BOARD_AVERMEDIA_M115] = { @@ -4167,6 +4163,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, @@ -5351,22 +5348,21 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, { switch (command) { case XC2028_TUNER_RESET: - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); - mdelay(250); - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0); - mdelay(250); - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); - mdelay(250); - saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); - saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); - saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); - saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); - saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, - 0x0001e000, 0x0001e000); - return 0; + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + break; + } + return 0; } return -EINVAL; } @@ -5553,9 +5549,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: - case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: case SAA7134_BOARD_AVERMEDIA_M115: - case SAA7134_BOARD_AVERMEDIA_A16D: /* power-down tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0); @@ -5565,6 +5559,18 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); msleep(10); break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + msleep(1); + dev->has_remote = SAA7134_REMOTE_GPIO; + break; case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: /* power-down tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); @@ -5615,7 +5621,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); printk("%s: %s: hybrid analog/dvb card\n" - "%s: Sorry, only the analog inputs are supported for now.\n", + "%s: Sorry, only analog s-video and composite input " + "are supported for now.\n", dev->name, card(dev).name, dev->name); break; } @@ -5675,6 +5682,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_AVERMEDIA_A16D: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ctl.demod = XC3028_FE_ZARLINK456; break; default: diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index eec127864fe3..2c19cd0113c8 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -864,7 +864,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; @@ -1065,11 +1064,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/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 2d16be2259db..341b101b0357 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -153,12 +153,12 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } -static int mt352_aver_a16d_init(struct dvb_frontend *fe) +static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0xe }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -167,12 +167,9 @@ static int mt352_aver_a16d_init(struct dvb_frontend *fe) mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); mt352_write(fe, agc_cfg, sizeof(agc_cfg)); mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); - return 0; } - - static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { @@ -215,14 +212,10 @@ static struct mt352_config avermedia_777 = { .demod_init = mt352_aver777_init, }; -static struct mt352_config avermedia_16d = { - .demod_address = 0xf, - .demod_init = mt352_aver_a16d_init, -}; - -static struct mt352_config avermedia_e506r_mt352_dev = { +static struct mt352_config avermedia_xc3028_mt352_dev = { .demod_address = (0x1e >> 1), .no_tuner = 1, + .demod_init = mt352_avermedia_xc3028_init, }; /* ================================================================== @@ -538,19 +531,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; } /* ------------------------------------------------------------------ */ @@ -971,9 +968,10 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_AVERMEDIA_A16D: - dprintk("avertv A16D dvb setup\n"); - dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_16d, - &dev->i2c_adap); + dprintk("AverMedia A16D dvb setup\n"); + dev->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); attach_xc3028 = 1; break; case SAA7134_BOARD_MD7134: @@ -997,7 +995,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: @@ -1022,36 +1022,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; } } } @@ -1067,15 +1083,21 @@ 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; } - } + } else + wprintk("failed to attach tda10046\n"); 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); @@ -1086,16 +1108,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 */ @@ -1117,19 +1143,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, @@ -1138,10 +1162,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; @@ -1168,66 +1194,95 @@ 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: + dprintk("AverMedia E506R dvb setup\n"); + saa7134_set_gpio(dev, 25, 0); + msleep(10); + saa7134_set_gpio(dev, 25, 1); dev->dvb.frontend = dvb_attach(mt352_attach, - &avermedia_e506r_mt352_dev, - &dev->i2c_adap); + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); attach_xc3028 = 1; - break; case SAA7134_BOARD_MD7134_BRIDGE_2: dev->dvb.frontend = dvb_attach(tda10086_attach, &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; @@ -1254,10 +1309,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; } } @@ -1282,6 +1334,13 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.sleep(dev->dvb.frontend); } return ret; + +dettach_frontend: + if (dev->dvb.frontend) + dvb_frontend_detach(dev->dvb.frontend); + dev->dvb.frontend = NULL; + + return -1; } static int dvb_fini(struct saa7134_dev *dev) diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 1314522a8130..3ae71a340822 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -110,9 +110,10 @@ static int ts_release(struct inode *inode, struct file *file) { struct saa7134_dev *dev = file->private_data; + mutex_lock(&dev->empress_tsq.vb_lock); + videobuf_stop(&dev->empress_tsq); videobuf_mmap_free(&dev->empress_tsq); - dev->empress_users--; /* stop the encoder */ ts_reset_encoder(dev); @@ -121,6 +122,10 @@ static int ts_release(struct inode *inode, struct file *file) saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + dev->empress_users--; + + mutex_unlock(&dev->empress_tsq.vb_lock); + return 0; } @@ -163,8 +168,7 @@ ts_mmap(struct file *file, struct vm_area_struct * vma) static int empress_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; strcpy(cap->driver, "saa7134"); strlcpy(cap->card, saa7134_boards[dev->board].name, @@ -219,8 +223,7 @@ static int empress_enum_fmt_cap(struct file *file, void *priv, static int empress_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f); @@ -233,8 +236,7 @@ static int empress_g_fmt_cap(struct file *file, void *priv, static int empress_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f); @@ -248,8 +250,7 @@ static int empress_s_fmt_cap(struct file *file, void *priv, static int empress_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_reqbufs(&dev->empress_tsq, p); } @@ -257,24 +258,21 @@ static int empress_reqbufs(struct file *file, void *priv, static int empress_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_querybuf(&dev->empress_tsq, b); } static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_qbuf(&dev->empress_tsq, b); } static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_dqbuf(&dev->empress_tsq, b, file->f_flags & O_NONBLOCK); @@ -283,8 +281,7 @@ static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int empress_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_streamon(&dev->empress_tsq); } @@ -292,8 +289,7 @@ static int empress_streamon(struct file *file, void *priv, static int empress_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_streamoff(&dev->empress_tsq); } @@ -301,8 +297,7 @@ static int empress_streamoff(struct file *file, void *priv, static int empress_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; /* count == 0 is abused in saa6752hs.c, so that special case is handled here explicitly. */ @@ -321,8 +316,7 @@ static int empress_s_ext_ctrls(struct file *file, void *priv, static int empress_g_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 919632b10aae..76e6501d238d 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -323,6 +323,15 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPMODE1, 0x1); saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; + case SAA7134_BOARD_AVERMEDIA_A16D: + ir_codes = ir_codes_avermedia_a16d; + mask_keycode = 0x02F200; + mask_keydown = 0x000400; + polling = 50; /* ms */ + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; case SAA7134_BOARD_KWORLD_TERMINATOR: ir_codes = ir_codes_pixelview; mask_keycode = 0x00001f; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index a0baf2d0ba7f..48e1a01718ec 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1634,7 +1634,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/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index 72c4081feff5..2220f9569941 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1429,8 +1429,6 @@ static int saa717x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - snprintf(client->name, sizeof(client->name) - 1, "saa717x"); - if (saa717x_write(client, 0x5a4, 0xfe) && saa717x_write(client, 0x5a5, 0x0f) && saa717x_write(client, 0x5a6, 0x00) && @@ -1507,6 +1505,12 @@ static int saa717x_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id saa717x_id[] = { + { "saa717x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa717x_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa717x", .driverid = I2C_DRIVERID_SAA717X, @@ -1514,4 +1518,5 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .probe = saa717x_probe, .remove = saa717x_remove, .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, + .id_table = saa717x_id, }; diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 5748b1e1a128..7f9c7bcf3c85 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -34,7 +34,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/page-flags.h> -#include <linux/byteorder/generic.h> +#include <asm/byteorder.h> #include <asm/page.h> #include <asm/uaccess.h> diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a1b92446c8b4..d015bfe00950 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -763,15 +763,6 @@ static struct device_driver ic_drv = { .owner = THIS_MODULE, }; -/* - * Image capture host - this is a host device, not a bus device, so, - * no bus reference, no probing. - */ -static struct class soc_camera_host_class = { - .owner = THIS_MODULE, - .name = "camera_host", -}; - static void dummy_release(struct device *dev) { } @@ -801,7 +792,6 @@ int soc_camera_host_register(struct soc_camera_host *ici) /* Number might be equal to the platform device ID */ sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); - ici->dev.class = &soc_camera_host_class; mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -1003,14 +993,9 @@ static int __init soc_camera_init(void) ret = driver_register(&ic_drv); if (ret) goto edrvr; - ret = class_register(&soc_camera_host_class); - if (ret) - goto eclr; return 0; -eclr: - driver_unregister(&ic_drv); edrvr: bus_unregister(&soc_camera_bus_type); return ret; @@ -1018,7 +1003,6 @@ edrvr: static void __exit soc_camera_exit(void) { - class_unregister(&soc_camera_host_class); driver_unregister(&ic_drv); bus_unregister(&soc_camera_bus_type); } diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 9276ed997388..b12c60cf5a09 100644 --- a/drivers/media/video/stk-webcam.c +++ b/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 <linux/videodev2.h> #include <media/v4l2-common.h> @@ -245,6 +246,8 @@ static int stk_initialise(struct stk_camera *dev) return -1; } +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /* sysfs functions */ /*FIXME cleanup this */ @@ -350,6 +353,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/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index e57a64605778..8f0100f67a91 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -885,12 +885,19 @@ static int __exit tcm825x_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id tcm825x_id[] = { + { "tcm825x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tcm825x_id); + static struct i2c_driver tcm825x_i2c_driver = { .driver = { .name = TCM825X_NAME, }, .probe = tcm825x_probe, .remove = __exit_p(tcm825x_remove), + .id_table = tcm825x_id, }; static struct tcm825x_sensor tcm825x = { diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index f1db54202dea..28ab9f9d760a 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -168,6 +168,11 @@ static int tlv320aic23b_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id tlv320aic23b_id[] = { + { "tlv320aic23b", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tlv320aic23b", @@ -175,4 +180,5 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .command = tlv320aic23b_command, .probe = tlv320aic23b_probe, .remove = tlv320aic23b_remove, + .id_table = tlv320aic23b_id, }; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 6bf104ea051d..0d12ace61665 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -40,11 +40,11 @@ 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"); \ } \ - symbol_put(FUNCTION); \ __r; \ }) @@ -92,6 +92,7 @@ struct tuner { unsigned int type; /* chip type id */ unsigned int config; int (*tuner_callback) (void *dev, int command, int arg); + const char *name; }; /* standard i2c insmod options */ @@ -330,26 +331,16 @@ static void tuner_i2c_address_check(struct tuner *t) tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n"); tuner_warn("will soon be dropped. This message indicates that your\n"); tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n", - t->i2c->name, t->i2c->addr); + t->name, t->i2c->addr); tuner_warn("To ensure continued support for your device, please\n"); tuner_warn("send a copy of this message, along with full dmesg\n"); tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n"); tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n"); tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n", - t->i2c->adapter->name, t->i2c->addr, t->type, t->i2c->name); + t->i2c->adapter->name, t->i2c->addr, t->type, t->name); tuner_warn("====================== WARNING! ======================\n"); } -static void attach_tda829x(struct tuner *t) -{ - struct tda829x_config cfg = { - .lna_cfg = t->config, - .tuner_callback = t->tuner_callback, - }; - dvb_attach(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,12 +376,19 @@ static void set_type(struct i2c_client *c, unsigned int type, switch (t->type) { case TUNER_MT2032: - dvb_attach(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: @@ -441,8 +439,9 @@ static void set_type(struct i2c_client *c, unsigned int type, break; } case TUNER_TDA9887: - dvb_attach(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: { @@ -450,10 +449,10 @@ static void set_type(struct i2c_client *c, unsigned int type, 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 (!dvb_attach(xc5000_attach, - &t->fe, t->i2c->adapter, &xc5000_cfg)) + &t->fe, t->i2c->adapter, &xc5000_cfg, + c->adapter->algo_data)) goto attach_failed; xc_tuner_ops = &t->fe.ops.tuner_ops; @@ -472,19 +471,17 @@ static void set_type(struct i2c_client *c, unsigned int type, 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->name = fe_tuner_ops->info.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)); + t->name = analog_ops->info.name; } - tuner_dbg("type set to %s\n", t->i2c->name); + tuner_dbg("type set to %s\n", t->name); if (t->mode_mask == T_UNINITIALIZED) t->mode_mask = new_mode_mask; @@ -539,7 +536,7 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) static inline int check_mode(struct tuner *t, char *cmd) { if ((1 << t->mode & t->mode_mask) == 0) { - return EINVAL; + return -EINVAL; } switch (t->mode) { @@ -733,11 +730,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; } @@ -779,13 +776,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) @@ -793,9 +790,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 */ @@ -813,10 +810,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)) @@ -830,9 +827,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); @@ -842,9 +839,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) { @@ -886,9 +883,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) { @@ -928,7 +925,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(); @@ -944,7 +941,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); @@ -955,7 +952,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; @@ -976,7 +973,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(); @@ -1023,7 +1020,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 +1114,7 @@ static int tuner_probe(struct i2c_client *client, if (NULL == t) return -ENOMEM; t->i2c = client; - strlcpy(client->name, "(tuner unset)", sizeof(client->name)); + t->name = "(tuner unset)"; i2c_set_clientdata(client, t); t->type = UNSET; t->audmode = V4L2_TUNER_MODE_STEREO; @@ -1167,7 +1164,7 @@ static int tuner_probe(struct i2c_client *client, /* If chip is not tda8290, don't register. since it can be tda9887*/ if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter, - t->i2c->addr) == 0) { + t->i2c->addr) >= 0) { tuner_dbg("tda829x detected\n"); } else { /* Default is being tda9887 */ @@ -1181,7 +1178,7 @@ static int tuner_probe(struct i2c_client *client, case 0x60: if (tuner_symbol_probe(tea5767_autodetection, t->i2c->adapter, t->i2c->addr) - != EINVAL) { + >= 0) { t->type = TUNER_TEA5767; t->mode_mask = T_RADIO; t->mode = T_STANDBY; @@ -1280,6 +1277,15 @@ static int tuner_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +/* This driver supports many devices and the idea is to let the driver + detect which device is present. So rather than listing all supported + devices here, we pretend to support a single, fake device type. */ +static const struct i2c_device_id tuner_id[] = { + { "tuner", }, /* autodetect */ + { } +}; +MODULE_DEVICE_TABLE(i2c, tuner_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tuner", .driverid = I2C_DRIVERID_TUNER, @@ -1289,6 +1295,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .suspend = tuner_suspend, .resume = tuner_resume, .legacy_probe = tuner_legacy_probe, + .id_table = tuner_id, }; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 6f9945b04e1f..c77914d99d15 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1505,7 +1505,8 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* fill required data structures */ - strcpy(client->name, desc->name); + if (!id) + strlcpy(client->name, desc->name, I2C_NAME_SIZE); chip->type = desc-chiplist; chip->shadow.count = desc->registers+1; chip->prevmode = -1; @@ -1830,6 +1831,15 @@ static int chip_legacy_probe(struct i2c_adapter *adap) return 0; } +/* This driver supports many devices and the idea is to let the driver + detect which device is present. So rather than listing all supported + devices here, we pretend to support a single, fake device type. */ +static const struct i2c_device_id chip_id[] = { + { "tvaudio", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, chip_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvaudio", .driverid = I2C_DRIVERID_TVAUDIO, @@ -1837,6 +1847,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .probe = chip_probe, .remove = chip_remove, .legacy_probe = chip_legacy_probe, + .id_table = chip_id, }; /* diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 3cf8a8e801e5..9da0e1807ffb 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -319,10 +319,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! */ @@ -341,8 +343,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/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index 93bfd19dec7d..b4628874933b 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -228,6 +228,11 @@ static int upd64031a_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id upd64031a_id[] = { + { "upd64031a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, upd64031a_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "upd64031a", @@ -235,4 +240,5 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .command = upd64031a_command, .probe = upd64031a_probe, .remove = upd64031a_remove, + .id_table = upd64031a_id, }; diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 9ab712a56ce0..9521ce004dcc 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -205,6 +205,11 @@ static int upd64083_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id upd64083_id[] = { + { "upd64083", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, upd64083_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "upd64083", @@ -212,4 +217,5 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .command = upd64083_command, .probe = upd64083_probe, .remove = upd64083_remove, + .id_table = upd64083_id, }; diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index 32e536edf09d..3d26a30abe1e 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -210,7 +210,7 @@ static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val) return ret; } -static int qcm_stv_setw(struct usb_device *dev, u16 reg, u16 val) +static int qcm_stv_setw(struct usb_device *dev, u16 reg, __le16 val) { int ret; diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig index 74e1d3075a20..fc24ef05b3f3 100644 --- a/drivers/media/video/usbvision/Kconfig +++ b/drivers/media/video/usbvision/Kconfig @@ -1,7 +1,7 @@ config VIDEO_USBVISION tristate "USB video devices based on Nogatech NT1003/1004/1005" depends on I2C && VIDEO_V4L2 - select MEDIA_TUNER + select VIDEO_TUNER select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO ---help--- There are more than 50 different USB video devices based on diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile new file mode 100644 index 000000000000..968c1994eda0 --- /dev/null +++ b/drivers/media/video/uvc/Makefile @@ -0,0 +1,3 @@ +uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ + uvc_status.o uvc_isight.o +obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c new file mode 100644 index 000000000000..f0ee46d15540 --- /dev/null +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -0,0 +1,1256 @@ +/* + * uvc_ctrl.c -- USB Video Class driver - Controls + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include "uvcvideo.h" + +#define UVC_CTRL_NDATA 2 +#define UVC_CTRL_DATA_CURRENT 0 +#define UVC_CTRL_DATA_BACKUP 1 + +/* ------------------------------------------------------------------------ + * Control, formats, ... + */ + +static struct uvc_control_info uvc_ctrls[] = { + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BRIGHTNESS_CONTROL, + .index = 0, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_CONTRAST_CONTROL, + .index = 1, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_CONTROL, + .index = 2, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SATURATION_CONTROL, + .index = 3, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SHARPNESS_CONTROL, + .index = 4, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAMMA_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .index = 8, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAIN_CONTROL, + .index = 9, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .index = 10, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_AUTO_CONTROL, + .index = 11, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_MODE_CONTROL, + .index = 1, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_PRIORITY_CONTROL, + .index = 2, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .index = 3, + .size = 4, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_AUTO_CONTROL, + .index = 17, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .index = 12, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .index = 6, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .index = 13, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .index = 7, + .size = 4, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, +}; + +static struct uvc_menu_info power_line_frequency_controls[] = { + { 0, "Disabled" }, + { 1, "50 Hz" }, + { 2, "60 Hz" }, +}; + +static struct uvc_menu_info exposure_auto_controls[] = { + { 1, "Manual Mode" }, + { 2, "Auto Mode" }, + { 4, "Shutter Priority Mode" }, + { 8, "Aperture Priority Mode" }, +}; + +static struct uvc_control_mapping uvc_ctrl_mappings[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BRIGHTNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_CONTRAST_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_HUE, + .name = "Hue", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SATURATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_SHARPNESS, + .name = "Sharpness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SHARPNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAMMA, + .name = "Gamma", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAMMA_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .name = "Backlight Compensation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAIN, + .name = "Gain", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAIN_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .name = "Power Line Frequency", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_count = ARRAY_SIZE(power_line_frequency_controls), + }, + { + .id = V4L2_CID_HUE_AUTO, + .name = "Hue, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_AUTO, + .name = "Exposure, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_MODE_CONTROL, + .size = 4, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_BITMASK, + .menu_info = exposure_auto_controls, + .menu_count = ARRAY_SIZE(exposure_auto_controls), + }, + { + .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, + .name = "Exposure, Auto Priority", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_PRIORITY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_ABSOLUTE, + .name = "Exposure (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Temperature, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .name = "White Balance Temperature", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Component, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .name = "White Balance Blue Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_RED_BALANCE, + .name = "White Balance Red Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .name = "Focus (absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_FOCUS_AUTO, + .name = "Focus, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) +{ + return ctrl->data + id * ctrl->info->size; +} + +static inline int uvc_get_bit(const __u8 *data, int bit) +{ + return (data[bit >> 3] >> (bit & 7)) & 1; +} + +/* Extract the bit string specified by mapping->offset and mapping->size + * from the little-endian data stored at 'data' and return the result as + * a signed 32bit integer. Sign extension will be performed if the mapping + * references a signed data type. + */ +static __s32 uvc_get_le_value(const __u8 *data, + struct uvc_control_mapping *mapping) +{ + int bits = mapping->size; + int offset = mapping->offset; + __s32 value = 0; + __u8 mask; + + data += offset / 8; + offset &= 7; + mask = ((1LL << bits) - 1) << offset; + + for (; bits > 0; data++) { + __u8 byte = *data & mask; + value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); + bits -= 8 - (offset > 0 ? offset : 0); + offset -= 8; + mask = (1 << bits) - 1; + } + + /* Sign-extend the value if needed */ + if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) + value |= -(value & (1 << (mapping->size - 1))); + + return value; +} + +/* Set the bit string specified by mapping->offset and mapping->size + * in the little-endian data stored at 'data' to the value 'value'. + */ +static void uvc_set_le_value(__s32 value, __u8 *data, + struct uvc_control_mapping *mapping) +{ + int bits = mapping->size; + int offset = mapping->offset; + __u8 mask; + + data += offset / 8; + offset &= 7; + + for (; bits > 0; data++) { + mask = ((1LL << bits) - 1) << offset; + *data = (*data & ~mask) | ((value << offset) & mask); + value >>= offset ? offset : 8; + bits -= 8 - offset; + offset = 0; + } +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; +static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const __u8 uvc_media_transport_input_guid[16] = + UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; + +static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16]) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case ITT_CAMERA: + return memcmp(uvc_camera_guid, guid, 16) == 0; + + case ITT_MEDIA_TRANSPORT_INPUT: + return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; + + case VC_PROCESSING_UNIT: + return memcmp(uvc_processing_guid, guid, 16) == 0; + + case VC_EXTENSION_UNIT: + return memcmp(entity->extension.guidExtensionCode, + guid, 16) == 0; + + default: + return 0; + } +} + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, + struct uvc_control_mapping **mapping, struct uvc_control **control, + int next) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *map; + unsigned int i; + + if (entity == NULL) + return; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL) + continue; + + list_for_each_entry(map, &ctrl->info->mappings, list) { + if ((map->id == v4l2_id) && !next) { + *control = ctrl; + *mapping = map; + return; + } + + if ((*mapping == NULL || (*mapping)->id > map->id) && + (map->id > v4l2_id) && next) { + *control = ctrl; + *mapping = map; + } + } + } +} + +struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping) +{ + struct uvc_control *ctrl = NULL; + struct uvc_entity *entity; + int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL; + + *mapping = NULL; + + /* Mask the query flags. */ + v4l2_id &= V4L2_CTRL_ID_MASK; + + /* Find the control. */ + __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + + list_for_each_entry(entity, &video->iterms, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + list_for_each_entry(entity, &video->extensions, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + if (ctrl == NULL && !next) + uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", + v4l2_id); + + return ctrl; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + struct uvc_menu_info *menu; + unsigned int i; + __u8 data[8]; + int ret; + + ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) + return -EINVAL; + + v4l2_ctrl->id = mapping->id; + v4l2_ctrl->type = mapping->v4l2_type; + strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); + v4l2_ctrl->flags = 0; + + if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { + if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + } + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = mapping->menu_count - 1; + v4l2_ctrl->step = 1; + + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == v4l2_ctrl->default_value) { + v4l2_ctrl->default_value = i; + break; + } + } + + return 0; + } + + if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { + if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + } + if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { + if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + } + if (ctrl->info->flags & UVC_CONTROL_GET_RES) { + if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->step = uvc_get_le_value(data, mapping); + } + + return 0; +} + + +/* -------------------------------------------------------------------------- + * Control transactions + * + * To make extended set operations as atomic as the hardware allows, controls + * are handled using begin/commit/rollback operations. + * + * At the beginning of a set request, uvc_ctrl_begin should be called to + * initialize the request. This function acquires the control lock. + * + * When setting a control, the new value is stored in the control data field + * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for + * later processing. If the UVC and V4L2 control sizes differ, the current + * value is loaded from the hardware before storing the new value in the data + * field. + * + * After processing all controls in the transaction, uvc_ctrl_commit or + * uvc_ctrl_rollback must be called to apply the pending changes to the + * hardware or revert them. When applying changes, all controls marked as + * dirty will be modified in the UVC device, and the dirty flag will be + * cleared. When reverting controls, the control data field + * UVC_CTRL_DATA_CURRENT is reverted to its previous value + * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the + * control lock. + */ +int uvc_ctrl_begin(struct uvc_video_device *video) +{ + return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; +} + +static int uvc_ctrl_commit_entity(struct uvc_device *dev, + struct uvc_entity *entity, int rollback) +{ + struct uvc_control *ctrl; + unsigned int i; + int ret; + + if (entity == NULL) + return 0; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL || !ctrl->dirty) + continue; + + if (!rollback) + ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id, + dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + else + ret = 0; + + if (rollback || ret < 0) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + ctrl->info->size); + + if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) + ctrl->loaded = 0; + + ctrl->dirty = 0; + + if (ret < 0) + return ret; + } + + return 0; +} + +int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) +{ + struct uvc_entity *entity; + int ret = 0; + + /* Find the control. */ + ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); + if (ret < 0) + goto done; + + list_for_each_entry(entity, &video->iterms, chain) { + ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + if (ret < 0) + goto done; + } + + list_for_each_entry(entity, &video->extensions, chain) { + ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + if (ret < 0) + goto done; + } + +done: + mutex_unlock(&video->ctrl_mutex); + return ret; +} + +int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + struct uvc_menu_info *menu; + unsigned int i; + int ret; + + ctrl = uvc_find_control(video, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) + return -EINVAL; + + if (!ctrl->loaded) { + ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + if (ret < 0) + return ret; + + if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) + ctrl->loaded = 1; + } + + xctrl->value = uvc_get_le_value( + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == xctrl->value) { + xctrl->value = i; + break; + } + } + } + + return 0; +} + +int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + s32 value = xctrl->value; + int ret; + + ctrl = uvc_find_control(video, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) + return -EINVAL; + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + if (value < 0 || value >= mapping->menu_count) + return -EINVAL; + value = mapping->menu_info[value].value; + } + + if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { + if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + 0, ctrl->info->size); + } else { + ret = uvc_query_ctrl(video->dev, GET_CUR, + ctrl->entity->id, video->dev->intfnum, + ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + if (ret < 0) + return ret; + } + + if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) + ctrl->loaded = 1; + } + + if (!ctrl->dirty) { + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + } + + uvc_set_le_value(value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + + ctrl->dirty = 1; + ctrl->modified = 1; + return 0; +} + +/* -------------------------------------------------------------------------- + * Dynamic controls + */ + +int uvc_xu_ctrl_query(struct uvc_video_device *video, + struct uvc_xu_control *xctrl, int set) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl = NULL; + unsigned int i, found = 0; + __u8 *data; + int ret; + + /* Find the extension unit. */ + list_for_each_entry(entity, &video->extensions, chain) { + if (entity->id == xctrl->unit) + break; + } + + if (entity->id != xctrl->unit) { + uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", + xctrl->unit); + return -EINVAL; + } + + /* Find the control. */ + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL) + continue; + + if (ctrl->info->selector == xctrl->selector) { + found = 1; + break; + } + } + + if (!found) { + uvc_trace(UVC_TRACE_CONTROL, + "Control " UVC_GUID_FORMAT "/%u not found.\n", + UVC_GUID_ARGS(entity->extension.guidExtensionCode), + xctrl->selector); + return -EINVAL; + } + + /* Validate control data size. */ + if (ctrl->info->size != xctrl->size) + return -EINVAL; + + if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) || + (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) + return -EINVAL; + + if (mutex_lock_interruptible(&video->ctrl_mutex)) + return -ERESTARTSYS; + + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + xctrl->size); + data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); + + if (set && copy_from_user(data, xctrl->data, xctrl->size)) { + ret = -EFAULT; + goto out; + } + + ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit, + video->dev->intfnum, xctrl->selector, data, + xctrl->size); + if (ret < 0) + goto out; + + if (!set && copy_to_user(xctrl->data, data, xctrl->size)) { + ret = -EFAULT; + goto out; + } + +out: + if (ret) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + xctrl->size); + + mutex_unlock(&video->ctrl_mutex); + return ret; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Restore control values after resume, skipping controls that haven't been + * changed. + * + * TODO + * - Don't restore modified controls that are back to their default value. + * - Handle restore order (Auto-Exposure Mode should be restored before + * Exposure Time). + */ +int uvc_ctrl_resume_device(struct uvc_device *dev) +{ + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + int ret; + + /* Walk the entities list and restore controls when possible. */ + list_for_each_entry(entity, &dev->entities, list) { + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + + if (ctrl->info == NULL || !ctrl->modified || + (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0) + continue; + + printk(KERN_INFO "restoring control " UVC_GUID_FORMAT + "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity), + ctrl->info->index, ctrl->info->selector); + ctrl->dirty = 1; + } + + ret = uvc_ctrl_commit_entity(dev, entity, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Control and mapping handling + */ + +static void uvc_ctrl_add_ctrl(struct uvc_device *dev, + struct uvc_control_info *info) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl = NULL; + int ret, found = 0; + unsigned int i; + + list_for_each_entry(entity, &dev->entities, list) { + if (!uvc_entity_match_guid(entity, info->entity)) + continue; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->index == info->index) { + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) + return; + + if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + /* Check if the device control information and length match + * the user supplied information. + */ + __u32 flags; + __le16 size; + __u8 inf; + + if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id, + dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) { + uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " + "control " UVC_GUID_FORMAT "/%u (%d).\n", + UVC_GUID_ARGS(info->entity), info->selector, + ret); + return; + } + + if (info->size != le16_to_cpu(size)) { + uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT + "/%u size doesn't match user supplied " + "value.\n", UVC_GUID_ARGS(info->entity), + info->selector); + return; + } + + if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id, + dev->intfnum, info->selector, &inf, 1)) < 0) { + uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " + "control " UVC_GUID_FORMAT "/%u (%d).\n", + UVC_GUID_ARGS(info->entity), info->selector, + ret); + return; + } + + flags = info->flags; + if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || + ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u flags don't match " + "supported operations.\n", + UVC_GUID_ARGS(info->entity), info->selector); + return; + } + } + + ctrl->info = info; + ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL); + uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u " + "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity), + ctrl->info->selector, dev->udev->devpath, entity->id); +} + +/* + * Add an item to the UVC control information list, and instantiate a control + * structure for each device that supports the control. + */ +int uvc_ctrl_add_info(struct uvc_control_info *info) +{ + struct uvc_control_info *ctrl; + struct uvc_device *dev; + int ret = 0; + + /* Find matching controls by walking the devices, entities and + * controls list. + */ + mutex_lock(&uvc_driver.ctrl_mutex); + + /* First check if the list contains a control matching the new one. + * Bail out if it does. + */ + list_for_each_entry(ctrl, &uvc_driver.controls, list) { + if (memcmp(ctrl->entity, info->entity, 16)) + continue; + + if (ctrl->selector == info->selector) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u is already defined.\n", + UVC_GUID_ARGS(info->entity), info->selector); + ret = -EEXIST; + goto end; + } + if (ctrl->index == info->index) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u would overwrite index " + "%d.\n", UVC_GUID_ARGS(info->entity), + info->selector, info->index); + ret = -EEXIST; + goto end; + } + } + + list_for_each_entry(dev, &uvc_driver.devices, list) + uvc_ctrl_add_ctrl(dev, info); + + INIT_LIST_HEAD(&info->mappings); + list_add_tail(&info->list, &uvc_driver.controls); +end: + mutex_unlock(&uvc_driver.ctrl_mutex); + return ret; +} + +int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) +{ + struct uvc_control_info *info; + struct uvc_control_mapping *map; + int ret = -EINVAL; + + if (mapping->id & ~V4L2_CTRL_ID_MASK) { + uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " + "invalid control id 0x%08x\n", mapping->name, + mapping->id); + return -EINVAL; + } + + mutex_lock(&uvc_driver.ctrl_mutex); + list_for_each_entry(info, &uvc_driver.controls, list) { + if (memcmp(info->entity, mapping->entity, 16) || + info->selector != mapping->selector) + continue; + + if (info->size * 8 < mapping->size + mapping->offset) { + uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would " + "overflow control " UVC_GUID_FORMAT "/%u\n", + mapping->name, UVC_GUID_ARGS(info->entity), + info->selector); + ret = -EOVERFLOW; + goto end; + } + + /* Check if the list contains a mapping matching the new one. + * Bail out if it does. + */ + list_for_each_entry(map, &info->mappings, list) { + if (map->id == mapping->id) { + uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is " + "already defined.\n", mapping->name); + ret = -EEXIST; + goto end; + } + } + + mapping->ctrl = info; + list_add_tail(&mapping->list, &info->mappings); + uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control " + UVC_GUID_FORMAT "/%u.\n", mapping->name, + UVC_GUID_ARGS(info->entity), info->selector); + + ret = 0; + break; + } +end: + mutex_unlock(&uvc_driver.ctrl_mutex); + return ret; +} + +/* + * Initialize device controls. + */ +int uvc_ctrl_init_device(struct uvc_device *dev) +{ + struct uvc_control_info *info; + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + + /* Walk the entities list and instantiate controls */ + list_for_each_entry(entity, &dev->entities, list) { + unsigned int bControlSize = 0, ncontrols = 0; + __u8 *bmControls = NULL; + + if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + bmControls = entity->extension.bmControls; + bControlSize = entity->extension.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) { + bmControls = entity->processing.bmControls; + bControlSize = entity->processing.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) { + bmControls = entity->camera.bmControls; + bControlSize = entity->camera.bControlSize; + } + + for (i = 0; i < bControlSize; ++i) + ncontrols += hweight8(bmControls[i]); + + if (ncontrols == 0) + continue; + + entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL); + if (entity->controls == NULL) + return -ENOMEM; + + entity->ncontrols = ncontrols; + + ctrl = entity->controls; + for (i = 0; i < bControlSize * 8; ++i) { + if (uvc_get_bit(bmControls, i) == 0) + continue; + + ctrl->entity = entity; + ctrl->index = i; + ctrl++; + } + } + + /* Walk the controls info list and associate them with the device + * controls, then add the device to the global device list. This has + * to be done while holding the controls lock, to make sure + * uvc_ctrl_add_info() will not get called in-between. + */ + mutex_lock(&uvc_driver.ctrl_mutex); + list_for_each_entry(info, &uvc_driver.controls, list) + uvc_ctrl_add_ctrl(dev, info); + + list_add_tail(&dev->list, &uvc_driver.devices); + mutex_unlock(&uvc_driver.ctrl_mutex); + + return 0; +} + +/* + * Cleanup device controls. + */ +void uvc_ctrl_cleanup_device(struct uvc_device *dev) +{ + struct uvc_entity *entity; + unsigned int i; + + /* Remove the device from the global devices list */ + mutex_lock(&uvc_driver.ctrl_mutex); + if (dev->list.next != NULL) + list_del(&dev->list); + mutex_unlock(&uvc_driver.ctrl_mutex); + + list_for_each_entry(entity, &dev->entities, list) { + for (i = 0; i < entity->ncontrols; ++i) + kfree(entity->controls[i].data); + + kfree(entity->controls); + } +} + +void uvc_ctrl_init(void) +{ + struct uvc_control_info *ctrl = uvc_ctrls; + struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls); + struct uvc_control_mapping *mapping = uvc_ctrl_mappings; + struct uvc_control_mapping *mend = + mapping + ARRAY_SIZE(uvc_ctrl_mappings); + + for (; ctrl < cend; ++ctrl) + uvc_ctrl_add_info(ctrl); + + for (; mapping < mend; ++mapping) + uvc_ctrl_add_mapping(mapping); +} diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c new file mode 100644 index 000000000000..60ced589f898 --- /dev/null +++ b/drivers/media/video/uvc/uvc_driver.c @@ -0,0 +1,1955 @@ +/* + * uvc_driver.c -- USB Video Class driver + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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 driver aims to support video input devices compliant with the 'USB + * Video Class' specification. + * + * The driver doesn't support the deprecated v4l1 interface. It implements the + * mmap capture method only, and doesn't do any image format conversion in + * software. If your user-space application doesn't support YUYV or MJPEG, fix + * it :-). Please note that the MJPEG data have been stripped from their + * Huffman tables (DHT marker), you will need to add it back if your JPEG + * codec can't handle MJPEG data. + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +#define DRIVER_AUTHOR "Laurent Pinchart <laurent.pinchart@skynet.be>" +#define DRIVER_DESC "USB Video Class driver" +#ifndef DRIVER_VERSION +#define DRIVER_VERSION "v0.1.0" +#endif + +static unsigned int uvc_quirks_param; +unsigned int uvc_trace_param; + +/* ------------------------------------------------------------------------ + * Control, formats, ... + */ + +static struct uvc_format_desc uvc_fmts[] = { + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:0 (NV12)", + .guid = UVC_GUID_FORMAT_NV12, + .fcc = V4L2_PIX_FMT_NV12, + }, + { + .name = "MJPEG", + .guid = UVC_GUID_FORMAT_MJPEG, + .fcc = V4L2_PIX_FMT_MJPEG, + }, + { + .name = "YVU 4:2:0 (YV12)", + .guid = UVC_GUID_FORMAT_YV12, + .fcc = V4L2_PIX_FMT_YVU420, + }, + { + .name = "YUV 4:2:0 (I420)", + .guid = UVC_GUID_FORMAT_I420, + .fcc = V4L2_PIX_FMT_YUV420, + }, + { + .name = "YUV 4:2:2 (UYVY)", + .guid = UVC_GUID_FORMAT_UYVY, + .fcc = V4L2_PIX_FMT_UYVY, + }, + { + .name = "Greyscale", + .guid = UVC_GUID_FORMAT_Y800, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "RGB Bayer", + .guid = UVC_GUID_FORMAT_BY8, + .fcc = V4L2_PIX_FMT_SBGGR8, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, + __u8 epaddr) +{ + struct usb_host_endpoint *ep; + unsigned int i; + + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + ep = &alts->endpoint[i]; + if (ep->desc.bEndpointAddress == epaddr) + return ep; + } + + return NULL; +} + +static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) +{ + unsigned int len = ARRAY_SIZE(uvc_fmts); + unsigned int i; + + for (i = 0; i < len; ++i) { + if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) + return &uvc_fmts[i]; + } + + return NULL; +} + +static __u32 uvc_colorspace(const __u8 primaries) +{ + static const __u8 colorprimaries[] = { + 0, + V4L2_COLORSPACE_SRGB, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, + }; + + if (primaries < ARRAY_SIZE(colorprimaries)) + return colorprimaries[primaries]; + + return 0; +} + +/* Simplify a fraction using a simple continued fraction decomposition. The + * idea here is to convert fractions such as 333333/10000000 to 1/30 using + * 32 bit arithmetic only. The algorithm is not perfect and relies upon two + * arbitrary parameters to remove non-significative terms from the simple + * continued fraction decomposition. Using 8 and 333 for n_terms and threshold + * respectively seems to give nice results. + */ +void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold) +{ + uint32_t *an; + uint32_t x, y, r; + unsigned int i, n; + + an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); + if (an == NULL) + return; + + /* Convert the fraction to a simple continued fraction. See + * http://mathforum.org/dr.math/faq/faq.fractions.html + * Stop if the current term is bigger than or equal to the given + * threshold. + */ + x = *numerator; + y = *denominator; + + for (n = 0; n < n_terms && y != 0; ++n) { + an[n] = x / y; + if (an[n] >= threshold) { + if (n < 2) + n++; + break; + } + + r = x - an[n] * y; + x = y; + y = r; + } + + /* Expand the simple continued fraction back to an integer fraction. */ + x = 0; + y = 1; + + for (i = n; i > 0; --i) { + r = y; + y = an[i-1] * y + x; + x = r; + } + + *numerator = y; + *denominator = x; + kfree(an); +} + +/* Convert a fraction to a frame interval in 100ns multiples. The idea here is + * to compute numerator / denominator * 10000000 using 32 bit fixed point + * arithmetic only. + */ +uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) +{ + uint32_t multiplier; + + /* Saturate the result if the operation would overflow. */ + if (denominator == 0 || + numerator/denominator >= ((uint32_t)-1)/10000000) + return (uint32_t)-1; + + /* Divide both the denominator and the multiplier by two until + * numerator * multiplier doesn't overflow. If anyone knows a better + * algorithm please let me know. + */ + multiplier = 10000000; + while (numerator > ((uint32_t)-1)/multiplier) { + multiplier /= 2; + denominator /= 2; + } + + return denominator ? numerator * multiplier / denominator : 0; +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) +{ + struct uvc_entity *entity; + + list_for_each_entry(entity, &dev->entities, list) { + if (entity->id == id) + return entity; + } + + return NULL; +} + +static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, + int id, struct uvc_entity *entity) +{ + unsigned int i; + + if (entity == NULL) + entity = list_entry(&dev->entities, struct uvc_entity, list); + + list_for_each_entry_continue(entity, &dev->entities, list) { + switch (UVC_ENTITY_TYPE(entity)) { + case TT_STREAMING: + if (entity->output.bSourceID == id) + return entity; + break; + + case VC_PROCESSING_UNIT: + if (entity->processing.bSourceID == id) + return entity; + break; + + case VC_SELECTOR_UNIT: + for (i = 0; i < entity->selector.bNrInPins; ++i) + if (entity->selector.baSourceID[i] == id) + return entity; + break; + + case VC_EXTENSION_UNIT: + for (i = 0; i < entity->extension.bNrInPins; ++i) + if (entity->extension.baSourceID[i] == id) + return entity; + break; + } + } + + return NULL; +} + +/* ------------------------------------------------------------------------ + * Descriptors handling + */ + +static int uvc_parse_format(struct uvc_device *dev, + struct uvc_streaming *streaming, struct uvc_format *format, + __u32 **intervals, unsigned char *buffer, int buflen) +{ + struct usb_interface *intf = streaming->intf; + struct usb_host_interface *alts = intf->cur_altsetting; + struct uvc_format_desc *fmtdesc; + struct uvc_frame *frame; + const unsigned char *start = buffer; + unsigned int interval; + unsigned int i, n; + __u8 ftype; + + format->type = buffer[2]; + format->index = buffer[3]; + + switch (buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_FRAME_BASED: + if (buflen < 27) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Find the format descriptor from its GUID. */ + fmtdesc = uvc_format_by_guid(&buffer[5]); + + if (fmtdesc != NULL) { + strncpy(format->name, fmtdesc->name, + sizeof format->name); + format->fcc = fmtdesc->fcc; + } else { + uvc_printk(KERN_INFO, "Unknown video format " + UVC_GUID_FORMAT "\n", + UVC_GUID_ARGS(&buffer[5])); + snprintf(format->name, sizeof format->name, + UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5])); + format->fcc = 0; + } + + format->bpp = buffer[21]; + if (buffer[2] == VS_FORMAT_UNCOMPRESSED) { + ftype = VS_FRAME_UNCOMPRESSED; + } else { + ftype = VS_FRAME_FRAME_BASED; + if (buffer[27]) + format->flags = UVC_FMT_FLAG_COMPRESSED; + } + break; + + case VS_FORMAT_MJPEG: + if (buflen < 11) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + strncpy(format->name, "MJPEG", sizeof format->name); + format->fcc = V4L2_PIX_FMT_MJPEG; + format->flags = UVC_FMT_FLAG_COMPRESSED; + format->bpp = 0; + ftype = VS_FRAME_MJPEG; + break; + + case VS_FORMAT_DV: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + switch (buffer[8] & 0x7f) { + case 0: + strncpy(format->name, "SD-DV", sizeof format->name); + break; + case 1: + strncpy(format->name, "SDL-DV", sizeof format->name); + break; + case 2: + strncpy(format->name, "HD-DV", sizeof format->name); + break; + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d: unknown DV format %u\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, buffer[8]); + return -EINVAL; + } + + strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", + sizeof format->name); + + format->fcc = V4L2_PIX_FMT_DV; + format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; + format->bpp = 0; + ftype = 0; + + /* Create a dummy frame descriptor. */ + frame = &format->frame[0]; + memset(&format->frame[0], 0, sizeof format->frame[0]); + frame->bFrameIntervalType = 1; + frame->dwDefaultFrameInterval = 1; + frame->dwFrameInterval = *intervals; + *(*intervals)++ = 1; + format->nframes = 1; + break; + + case VS_FORMAT_MPEG2TS: + case VS_FORMAT_STREAM_BASED: + /* Not supported yet. */ + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d unsupported format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[2]); + return -EINVAL; + } + + uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); + + buflen -= buffer[0]; + buffer += buffer[0]; + + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame + * based formats have frame descriptors. + */ + while (buflen > 2 && buffer[2] == ftype) { + frame = &format->frame[format->nframes]; + + if (ftype != VS_FRAME_FRAME_BASED) + n = buflen > 25 ? buffer[25] : 0; + else + n = buflen > 21 ? buffer[21] : 0; + + n = n ? n : 3; + + if (buflen < 26 + 4*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FRAME error\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + frame->bFrameIndex = buffer[3]; + frame->bmCapabilities = buffer[4]; + frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); + frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]); + frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]); + frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]); + if (ftype != VS_FRAME_FRAME_BASED) { + frame->dwMaxVideoFrameBufferSize = + le32_to_cpup((__le32 *)&buffer[17]); + frame->dwDefaultFrameInterval = + le32_to_cpup((__le32 *)&buffer[21]); + frame->bFrameIntervalType = buffer[25]; + } else { + frame->dwMaxVideoFrameBufferSize = 0; + frame->dwDefaultFrameInterval = + le32_to_cpup((__le32 *)&buffer[17]); + frame->bFrameIntervalType = buffer[21]; + } + frame->dwFrameInterval = *intervals; + + /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize + * completely. Observed behaviours range from setting the + * value to 1.1x the actual frame size of hardwiring the + * 16 low bits to 0. This results in a higher than necessary + * memory usage as well as a wrong image size information. For + * uncompressed formats this can be fixed by computing the + * value from the frame size. + */ + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) + frame->dwMaxVideoFrameBufferSize = format->bpp + * frame->wWidth * frame->wHeight / 8; + + /* Some bogus devices report dwMinFrameInterval equal to + * dwMaxFrameInterval and have dwFrameIntervalStep set to + * zero. Setting all null intervals to 1 fixes the problem and + * some other divisions by zero which could happen. + */ + for (i = 0; i < n; ++i) { + interval = le32_to_cpup((__le32 *)&buffer[26+4*i]); + *(*intervals)++ = interval ? interval : 1; + } + + /* Make sure that the default frame interval stays between + * the boundaries. + */ + n -= frame->bFrameIntervalType ? 1 : 2; + frame->dwDefaultFrameInterval = + min(frame->dwFrameInterval[n], + max(frame->dwFrameInterval[0], + frame->dwDefaultFrameInterval)); + + uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000/frame->dwDefaultFrameInterval, + (100000000/frame->dwDefaultFrameInterval)%10); + + format->nframes++; + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { + if (buflen < 6) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d COLORFORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + format->colorspace = uvc_colorspace(buffer[3]); + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + return buffer - start; +} + +static int uvc_parse_streaming(struct uvc_device *dev, + struct usb_interface *intf) +{ + struct uvc_streaming *streaming = NULL; + struct uvc_format *format; + struct uvc_frame *frame; + struct usb_host_interface *alts = &intf->altsetting[0]; + unsigned char *_buffer, *buffer = alts->extra; + int _buflen, buflen = alts->extralen; + unsigned int nformats = 0, nframes = 0, nintervals = 0; + unsigned int size, i, n, p; + __u32 *interval; + __u16 psize; + int ret = -EINVAL; + + if (intf->cur_altsetting->desc.bInterfaceSubClass + != SC_VIDEOSTREAMING) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " + "video streaming interface\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " + "claimed\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + streaming = kzalloc(sizeof *streaming, GFP_KERNEL); + if (streaming == NULL) { + usb_driver_release_interface(&uvc_driver.driver, intf); + return -EINVAL; + } + + mutex_init(&streaming->mutex); + streaming->intf = usb_get_intf(intf); + streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + + /* The Pico iMage webcam has its class-specific interface descriptors + * after the endpoint descriptors. + */ + if (buflen == 0) { + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + struct usb_host_endpoint *ep = &alts->endpoint[i]; + + if (ep->extralen == 0) + continue; + + if (ep->extralen > 2 && + ep->extra[1] == USB_DT_CS_INTERFACE) { + uvc_trace(UVC_TRACE_DESCR, "trying extra data " + "from endpoint %u.\n", i); + buffer = alts->endpoint[i].extra; + buflen = alts->endpoint[i].extralen; + break; + } + } + } + + /* Skip the standard interface descriptors. */ + while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen <= 2) { + uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " + "interface descriptors found.\n"); + goto error; + } + + /* Parse the header descriptor. */ + if (buffer[2] == VS_OUTPUT_HEADER) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d OUTPUT HEADER descriptor is not supported.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } else if (buffer[2] == VS_INPUT_HEADER) { + p = buflen >= 5 ? buffer[3] : 0; + n = buflen >= 12 ? buffer[12] : 0; + + if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d INPUT HEADER descriptor is " + "invalid.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + streaming->header.bNumFormats = p; + streaming->header.bEndpointAddress = buffer[6]; + streaming->header.bmInfo = buffer[7]; + streaming->header.bTerminalLink = buffer[8]; + streaming->header.bStillCaptureMethod = buffer[9]; + streaming->header.bTriggerSupport = buffer[10]; + streaming->header.bTriggerUsage = buffer[11]; + streaming->header.bControlSize = n; + + streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + if (streaming->header.bmaControls == NULL) { + ret = -ENOMEM; + goto error; + } + + memcpy(streaming->header.bmaControls, &buffer[13], p*n); + } else { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d HEADER descriptor not found.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + + _buffer = buffer; + _buflen = buflen; + + /* Count the format and frame descriptors. */ + while (_buflen > 2) { + switch (_buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_MJPEG: + case VS_FORMAT_FRAME_BASED: + nformats++; + break; + + case VS_FORMAT_DV: + /* DV format has no frame descriptor. We will create a + * dummy frame descriptor with a dummy frame interval. + */ + nformats++; + nframes++; + nintervals++; + break; + + case VS_FORMAT_MPEG2TS: + case VS_FORMAT_STREAM_BASED: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT %u is not supported.\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, _buffer[2]); + break; + + case VS_FRAME_UNCOMPRESSED: + case VS_FRAME_MJPEG: + nframes++; + if (_buflen > 25) + nintervals += _buffer[25] ? _buffer[25] : 3; + break; + + case VS_FRAME_FRAME_BASED: + nframes++; + if (_buflen > 21) + nintervals += _buffer[21] ? _buffer[21] : 3; + break; + } + + _buflen -= _buffer[0]; + _buffer += _buffer[0]; + } + + if (nformats == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has no supported formats defined.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } + + size = nformats * sizeof *format + nframes * sizeof *frame + + nintervals * sizeof *interval; + format = kzalloc(size, GFP_KERNEL); + if (format == NULL) { + ret = -ENOMEM; + goto error; + } + + frame = (struct uvc_frame *)&format[nformats]; + interval = (__u32 *)&frame[nframes]; + + streaming->format = format; + streaming->nformats = nformats; + + /* Parse the format descriptors. */ + while (buflen > 2) { + switch (buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_MJPEG: + case VS_FORMAT_DV: + case VS_FORMAT_FRAME_BASED: + format->frame = frame; + ret = uvc_parse_format(dev, streaming, format, + &interval, buffer, buflen); + if (ret < 0) + goto error; + + frame += format->nframes; + format++; + + buflen -= ret; + buffer += ret; + continue; + + default: + break; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Parse the alternate settings to find the maximum bandwidth. */ + for (i = 0; i < intf->num_altsetting; ++i) { + struct usb_host_endpoint *ep; + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize > streaming->maxpsize) + streaming->maxpsize = psize; + } + + list_add_tail(&streaming->list, &dev->streaming); + return 0; + +error: + usb_driver_release_interface(&uvc_driver.driver, intf); + usb_put_intf(intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + return ret; +} + +/* Parse vendor-specific extensions. */ +static int uvc_parse_vendor_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + struct uvc_entity *unit; + unsigned int n, p; + int handled = 0; + + switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { + case 0x046d: /* Logitech */ + if (buffer[1] != 0x41 || buffer[2] != 0x01) + break; + + /* Logitech implements several vendor specific functions + * through vendor specific extension units (LXU). + * + * The LXU descriptors are similar to XU descriptors + * (see "USB Device Video Class for Video Devices", section + * 3.7.2.6 "Extension Unit Descriptor") with the following + * differences: + * + * ---------------------------------------------------------- + * 0 bLength 1 Number + * Size of this descriptor, in bytes: 24+p+n*2 + * ---------------------------------------------------------- + * 23+p+n bmControlsType N Bitmap + * Individual bits in the set are defined: + * 0: Absolute + * 1: Relative + * + * This bitset is mapped exactly the same as bmControls. + * ---------------------------------------------------------- + * 23+p+n*2 bReserved 1 Boolean + * ---------------------------------------------------------- + * 24+p+n*2 iExtension 1 Index + * Index of a string descriptor that describes this + * extension unit. + * ---------------------------------------------------------- + */ + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 25 + p ? buffer[22+p] : 0; + + if (buflen < 25 + p + 2*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + break; + } + + unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = VC_EXTENSION_UNIT; + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + unit->extension.bNrInPins = + le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit + + p + n; + memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); + + if (buffer[24+p+2*n] != 0) + usb_string(udev, buffer[24+p+2*n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + handled = 1; + break; + } + + return handled; +} + +static int uvc_parse_standard_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct uvc_entity *unit, *term; + struct usb_interface *intf; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned int i, n, p, len; + __u16 type; + + switch (buffer[2]) { + case VC_HEADER: + n = buflen >= 12 ? buffer[11] : 0; + + if (buflen < 12 || buflen < 12 + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d HEADER error\n", udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]); + dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]); + + /* Parse all USB Video Streaming interfaces. */ + for (i = 0; i < n; ++i) { + intf = usb_ifnum_to_if(udev, buffer[12+i]); + if (intf == NULL) { + uvc_trace(UVC_TRACE_DESCR, "device %d " + "interface %d doesn't exists\n", + udev->devnum, i); + continue; + } + + uvc_parse_streaming(dev, intf); + } + break; + + case VC_INPUT_TERMINAL: + if (buflen < 8) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = le16_to_cpup((__le16 *)&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, + buffer[3], type); + return 0; + } + + n = 0; + p = 0; + len = 8; + + if (type == ITT_CAMERA) { + n = buflen >= 15 ? buffer[14] : 0; + len = 15; + + } else if (type == ITT_MEDIA_TRANSPORT_INPUT) { + n = buflen >= 9 ? buffer[8] : 0; + p = buflen >= 10 + n ? buffer[9+n] : 0; + len = 10; + } + + if (buflen < len + n + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + term->id = buffer[3]; + term->type = type | UVC_TERM_INPUT; + + if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) { + term->camera.bControlSize = n; + term->camera.bmControls = (__u8 *)term + sizeof *term; + term->camera.wObjectiveFocalLengthMin = + le16_to_cpup((__le16 *)&buffer[8]); + term->camera.wObjectiveFocalLengthMax = + le16_to_cpup((__le16 *)&buffer[10]); + term->camera.wOcularFocalLength = + le16_to_cpup((__le16 *)&buffer[12]); + memcpy(term->camera.bmControls, &buffer[15], n); + } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { + term->media.bControlSize = n; + term->media.bmControls = (__u8 *)term + sizeof *term; + term->media.bTransportModeSize = p; + term->media.bmTransportModes = (__u8 *)term + + sizeof *term + n; + memcpy(term->media.bmControls, &buffer[9], n); + memcpy(term->media.bmTransportModes, &buffer[10+n], p); + } + + if (buffer[7] != 0) + usb_string(udev, buffer[7], term->name, + sizeof term->name); + else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) + sprintf(term->name, "Camera %u", buffer[3]); + else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) + sprintf(term->name, "Media %u", buffer[3]); + else + sprintf(term->name, "Input %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case VC_OUTPUT_TERMINAL: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = le16_to_cpup((__le16 *)&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, buffer[3], type); + return 0; + } + + term = kzalloc(sizeof *term, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + term->id = buffer[3]; + term->type = type | UVC_TERM_OUTPUT; + term->output.bSourceID = buffer[7]; + + if (buffer[8] != 0) + usb_string(udev, buffer[8], term->name, + sizeof term->name); + else + sprintf(term->name, "Output %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case VC_SELECTOR_UNIT: + p = buflen >= 5 ? buffer[4] : 0; + + if (buflen < 5 || buflen < 6 + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d SELECTOR_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + unit->selector.bNrInPins = buffer[4]; + unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->selector.baSourceID, &buffer[5], p); + + if (buffer[5+p] != 0) + usb_string(udev, buffer[5+p], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Selector %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case VC_PROCESSING_UNIT: + n = buflen >= 8 ? buffer[7] : 0; + p = dev->uvc_version >= 0x0110 ? 10 : 9; + + if (buflen < p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d PROCESSING_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + unit->processing.bSourceID = buffer[4]; + unit->processing.wMaxMultiplier = + le16_to_cpup((__le16 *)&buffer[5]); + unit->processing.bControlSize = buffer[7]; + unit->processing.bmControls = (__u8 *)unit + sizeof *unit; + memcpy(unit->processing.bmControls, &buffer[8], n); + if (dev->uvc_version >= 0x0110) + unit->processing.bmVideoStandards = buffer[9+n]; + + if (buffer[8+n] != 0) + usb_string(udev, buffer[8+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Processing %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case VC_EXTENSION_UNIT: + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 24 + p ? buffer[22+p] : 0; + + if (buflen < 24 + p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + unit->extension.bNrInPins = + le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + memcpy(unit->extension.bmControls, &buffer[23+p], n); + + if (buffer[23+p+n] != 0) + usb_string(udev, buffer[23+p+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " + "descriptor (%u)\n", buffer[2]); + break; + } + + return 0; +} + +static int uvc_parse_control(struct uvc_device *dev) +{ + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned char *buffer = alts->extra; + int buflen = alts->extralen; + int ret; + + /* Parse the default alternate setting only, as the UVC specification + * defines a single alternate setting, the default alternate setting + * zero. + */ + + while (buflen > 2) { + if (uvc_parse_vendor_control(dev, buffer, buflen) || + buffer[1] != USB_DT_CS_INTERFACE) + goto next_descriptor; + + if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) + return ret; + +next_descriptor: + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Check if the optional status endpoint is present. */ + if (alts->desc.bNumEndpoints == 1) { + struct usb_host_endpoint *ep = &alts->endpoint[0]; + struct usb_endpoint_descriptor *desc = &ep->desc; + + if (usb_endpoint_is_int_in(desc) && + le16_to_cpu(desc->wMaxPacketSize) >= 8 && + desc->bInterval != 0) { + uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " + "(addr %02x).\n", desc->bEndpointAddress); + dev->int_ep = ep; + } + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * USB probe and disconnect + */ + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + if (dev->video.vdev) { + if (dev->video.vdev->minor == -1) + video_device_release(dev->video.vdev); + else + video_unregister_device(dev->video.vdev); + dev->video.vdev = NULL; + } +} + +/* + * Scan the UVC descriptors to locate a chain starting at an Output Terminal + * and containing the following units: + * + * - a USB Streaming Output Terminal + * - zero or one Processing Unit + * - zero, one or mode single-input Selector Units + * - zero or one multiple-input Selector Units, provided all inputs are + * connected to input terminals + * - zero, one or mode single-input Extension Units + * - one Camera Input Terminal, or one or more External terminals. + * + * A side forward scan is made on each detected entity to check for additional + * extension units. + */ +static int uvc_scan_chain_entity(struct uvc_video_device *video, + struct uvc_entity *entity) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case VC_EXTENSION_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- XU %d", entity->id); + + if (entity->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " + "than 1 input pin.\n", entity->id); + return -1; + } + + list_add_tail(&entity->chain, &video->extensions); + break; + + case VC_PROCESSING_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- PU %d", entity->id); + + if (video->processing != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple " + "Processing Units in chain.\n"); + return -1; + } + + video->processing = entity; + break; + + case VC_SELECTOR_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- SU %d", entity->id); + + /* Single-input selector units are ignored. */ + if (entity->selector.bNrInPins == 1) + break; + + if (video->selector != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " + "Units in chain.\n"); + return -1; + } + + video->selector = entity; + break; + + case ITT_VENDOR_SPECIFIC: + case ITT_CAMERA: + case ITT_MEDIA_TRANSPORT_INPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + + list_add_tail(&entity->chain, &video->iterms); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " + "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); + return -1; + } + + return 0; +} + +static int uvc_scan_chain_forward(struct uvc_video_device *video, + struct uvc_entity *entity, struct uvc_entity *prev) +{ + struct uvc_entity *forward; + int found; + + /* Forward scan */ + forward = NULL; + found = 0; + + while (1) { + forward = uvc_entity_by_reference(video->dev, entity->id, + forward); + if (forward == NULL) + break; + + if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT || + forward == prev) + continue; + + if (forward->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has" + "more than 1 input pin.\n", entity->id); + return -1; + } + + list_add_tail(&forward->chain, &video->extensions); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (-> XU"); + + printk(" %d", forward->id); + found = 1; + } + } + if (found) + printk(")"); + + return 0; +} + +static int uvc_scan_chain_backward(struct uvc_video_device *video, + struct uvc_entity *entity) +{ + struct uvc_entity *term; + int id = -1, i; + + switch (UVC_ENTITY_TYPE(entity)) { + case VC_EXTENSION_UNIT: + id = entity->extension.baSourceID[0]; + break; + + case VC_PROCESSING_UNIT: + id = entity->processing.bSourceID; + break; + + case VC_SELECTOR_UNIT: + /* Single-input selector units are ignored. */ + if (entity->selector.bNrInPins == 1) { + id = entity->selector.baSourceID[0]; + break; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT"); + + video->selector = entity; + for (i = 0; i < entity->selector.bNrInPins; ++i) { + id = entity->selector.baSourceID[i]; + term = uvc_entity_by_id(video->dev, id); + if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { + uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " + "input %d isn't connected to an " + "input terminal\n", entity->id, i); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" %d", term->id); + + list_add_tail(&term->chain, &video->iterms); + uvc_scan_chain_forward(video, term, entity); + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk("\n"); + + id = 0; + break; + } + + return id; +} + +static int uvc_scan_chain(struct uvc_video_device *video) +{ + struct uvc_entity *entity, *prev; + int id; + + entity = video->oterm; + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + id = entity->output.bSourceID; + while (id != 0) { + prev = entity; + entity = uvc_entity_by_id(video->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -1; + } + + /* Process entity */ + if (uvc_scan_chain_entity(video, entity) < 0) + return -1; + + /* Forward scan */ + if (uvc_scan_chain_forward(video, entity, prev) < 0) + return -1; + + /* Stop when a terminal is found. */ + if (!UVC_ENTITY_IS_UNIT(entity)) + break; + + /* Backward scan */ + id = uvc_scan_chain_backward(video, entity); + if (id < 0) + return id; + } + + /* Initialize the video buffers queue. */ + uvc_queue_init(&video->queue); + + return 0; +} + +/* + * Register the video devices. + * + * The driver currently supports a single video device per control interface + * only. The terminal and units must match the following structure: + * + * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING + * + * The Extension Units, if present, must have a single input pin. The + * Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. + */ +static int uvc_register_video(struct uvc_device *dev) +{ + struct video_device *vdev; + struct uvc_entity *term; + int found = 0, ret; + + /* Check if the control interface matches the structure we expect. */ + list_for_each_entry(term, &dev->entities, list) { + struct uvc_streaming *streaming; + + if (UVC_ENTITY_TYPE(term) != TT_STREAMING) + continue; + + memset(&dev->video, 0, sizeof dev->video); + mutex_init(&dev->video.ctrl_mutex); + INIT_LIST_HEAD(&dev->video.iterms); + INIT_LIST_HEAD(&dev->video.extensions); + dev->video.oterm = term; + dev->video.dev = dev; + if (uvc_scan_chain(&dev->video) < 0) + continue; + + list_for_each_entry(streaming, &dev->streaming, list) { + if (streaming->header.bTerminalLink == term->id) { + dev->video.streaming = streaming; + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) { + uvc_printk(KERN_INFO, "No valid video chain found.\n"); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) { + uvc_printk(KERN_INFO, "Found a valid video chain ("); + list_for_each_entry(term, &dev->video.iterms, chain) { + printk("%d", term->id); + if (term->chain.next != &dev->video.iterms) + printk(","); + } + printk(" -> %d).\n", dev->video.oterm->id); + } + + /* Initialize the streaming interface with default streaming + * parameters. + */ + if ((ret = uvc_video_init(&dev->video)) < 0) { + uvc_printk(KERN_ERR, "Failed to initialize the device " + "(%d).\n", ret); + return ret; + } + + /* Register the device with V4L. */ + vdev = video_device_alloc(); + if (vdev == NULL) + return -1; + + /* We already hold a reference to dev->udev. The video device will be + * unregistered before the reference is released, so we don't need to + * get another one. + */ + vdev->dev = &dev->intf->dev; + vdev->type = 0; + vdev->type2 = 0; + vdev->minor = -1; + vdev->fops = &uvc_fops; + vdev->release = video_device_release; + strncpy(vdev->name, dev->name, sizeof vdev->name); + + /* Set the driver data before calling video_register_device, otherwise + * uvc_v4l2_open might race us. + * + * FIXME: usb_set_intfdata hasn't been called so far. Is that a + * problem ? Does any function which could be called here get + * a pointer to the usb_interface ? + */ + dev->video.vdev = vdev; + video_set_drvdata(vdev, &dev->video); + + if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { + dev->video.vdev = NULL; + video_device_release(vdev); + return -1; + } + + return 0; +} + +/* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * Unregistering the video devices is done here because every opened instance + * must be closed before the device can be unregistered. An alternative would + * have been to use another reference count for uvc_v4l2_open/uvc_release, and + * unregister the video devices on disconnect when that reference count drops + * to zero. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +void uvc_delete(struct kref *kref) +{ + struct uvc_device *dev = container_of(kref, struct uvc_device, kref); + struct list_head *p, *n; + + /* Unregister the video device */ + uvc_unregister_video(dev); + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streaming) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static int uvc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct uvc_device *dev; + int ret; + + if (id->idVendor && id->idProduct) + uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " + "(%04x:%04x)\n", udev->devpath, id->idVendor, + id->idProduct); + else + uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", + udev->devpath); + + /* Allocate memory for the device and initialize it */ + if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->entities); + INIT_LIST_HEAD(&dev->streaming); + kref_init(&dev->kref); + + dev->udev = usb_get_dev(udev); + dev->intf = usb_get_intf(intf); + dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + dev->quirks = id->driver_info | uvc_quirks_param; + + if (udev->product != NULL) + strncpy(dev->name, udev->product, sizeof dev->name); + else + snprintf(dev->name, sizeof dev->name, + "UVC Camera (%04x:%04x)", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + /* Parse the Video Class control descriptor */ + if (uvc_parse_control(dev) < 0) { + uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " + "descriptors.\n"); + goto error; + } + + uvc_printk(KERN_INFO, "Found UVC %u.%02u device %s (%04x:%04x)\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff, + udev->product ? udev->product : "<unnamed>", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (uvc_quirks_param != 0) { + uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module " + "parameter for testing purpose.\n", uvc_quirks_param); + uvc_printk(KERN_INFO, "Please report required quirks to the " + "linux-uvc-devel mailing list.\n"); + } + + /* Initialize controls */ + if (uvc_ctrl_init_device(dev) < 0) + goto error; + + /* Register the video devices */ + if (uvc_register_video(dev) < 0) + goto error; + + /* Save our data pointer in the interface data */ + usb_set_intfdata(intf, dev); + + /* Initialize the interrupt URB */ + if ((ret = uvc_status_init(dev)) < 0) { + uvc_printk(KERN_INFO, "Unable to initialize the status " + "endpoint (%d), status interrupt will not be " + "supported.\n", ret); + } + + uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); + return 0; + +error: + kref_put(&dev->kref, uvc_delete); + return -ENODEV; +} + +static void uvc_disconnect(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + /* Set the USB interface data to NULL. This can be done outside the + * lock, as there's no other reader. + */ + usb_set_intfdata(intf, NULL); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING) + return; + + /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide + * lock is needed to prevent uvc_disconnect from releasing its + * reference to the uvc_device instance after uvc_v4l2_open() received + * the pointer to the device (video_devdata) but before it got the + * chance to increase the reference count (kref_get). + */ + mutex_lock(&uvc_driver.open_mutex); + + dev->state |= UVC_DEV_DISCONNECTED; + kref_put(&dev->kref, uvc_delete); + + mutex_unlock(&uvc_driver.open_mutex); +} + +static int uvc_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + /* Controls are cached on the fly so they don't need to be saved. */ + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) + return uvc_status_suspend(dev); + + if (dev->video.streaming->intf != intf) { + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " + "interface mismatch.\n"); + return -EINVAL; + } + + return uvc_video_suspend(&dev->video); +} + +static int uvc_resume(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + int ret; + + uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { + if ((ret = uvc_ctrl_resume_device(dev)) < 0) + return ret; + + return uvc_status_resume(dev); + } + + if (dev->video.streaming->intf != intf) { + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " + "interface mismatch.\n"); + return -EINVAL; + } + + return uvc_video_resume(&dev->video); +} + +/* ------------------------------------------------------------------------ + * Driver initialization and cleanup + */ + +/* + * The Logitech cameras listed below have their interface class set to + * VENDOR_SPEC because they don't announce themselves as UVC devices, even + * though they are compliant. + */ +static struct usb_device_id uvc_ids[] = { + /* ALi M5606 (Clevo M540SR) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0402, + .idProduct = 0x5606, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Creative Live! Optia */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x041e, + .idProduct = 0x4057, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam NX-6000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x00f8, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam VX-7000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x0723, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Logitech Quickcam Fusion */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c1, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Orbit MP */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro for Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro 5000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c5, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Dell Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c6, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Cisco VT Camera II */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c7, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Apple Built-In iSight */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05ac, + .idProduct = 0x8501, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_BUILTIN_ISIGHT }, + /* Genesys Logic USB 2.0 PC Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05e3, + .idProduct = 0x0505, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Silicon Motion SM371 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x090c, + .idProduct = 0xb371, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* MT6227 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0e8d, + .idProduct = 0x0004, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Syntek (HP Spartan) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5212, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Asus U3S) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a33, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Ecamm Pico iMage */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18cd, + .idProduct = 0xcafe, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Bodelin ProScopeHR */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_DEV_HI + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19ab, + .idProduct = 0x1000, + .bcdDevice_hi = 0x0126, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* SiGma Micro USB Web Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1c4f, + .idProduct = 0x3000, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, + /* Acer OEM Webcam - Unknown vendor */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0100, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Packard Bell OEM Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0101, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Acer Crystal Eye webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0102, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Acer OrbiCam - Unknown vendor */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0200, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, uvc_ids); + +struct uvc_driver uvc_driver = { + .driver = { + .name = "uvcvideo", + .probe = uvc_probe, + .disconnect = uvc_disconnect, + .suspend = uvc_suspend, + .resume = uvc_resume, + .id_table = uvc_ids, + .supports_autosuspend = 1, + }, +}; + +static int __init uvc_init(void) +{ + int result; + + INIT_LIST_HEAD(&uvc_driver.devices); + INIT_LIST_HEAD(&uvc_driver.controls); + mutex_init(&uvc_driver.open_mutex); + mutex_init(&uvc_driver.ctrl_mutex); + + uvc_ctrl_init(); + + result = usb_register(&uvc_driver.driver); + if (result == 0) + printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); + return result; +} + +static void __exit uvc_cleanup(void) +{ + usb_deregister(&uvc_driver.driver); +} + +module_init(uvc_init); +module_exit(uvc_cleanup); + +module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(quirks, "Forced device quirks"); +module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c new file mode 100644 index 000000000000..37bdefdbead5 --- /dev/null +++ b/drivers/media/video/uvc/uvc_isight.c @@ -0,0 +1,134 @@ +/* + * uvc_isight.c -- USB Video Class driver - iSight support + * + * Copyright (C) 2006-2007 + * Ivan N. Zlatev <contact@i-nz.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. + * + */ + +#include <linux/usb.h> +#include <linux/kernel.h> +#include <linux/mm.h> + +#include "uvcvideo.h" + +/* Built-in iSight webcams implements most of UVC 1.0 except a + * different packet format. Instead of sending a header at the + * beginning of each isochronous transfer payload, the webcam sends a + * single header per image (on its own in a packet), followed by + * packets containing data only. + * + * Offset Size (bytes) Description + * ------------------------------------------------------------------ + * 0x00 1 Header length + * 0x01 1 Flags (UVC-compliant) + * 0x02 4 Always equal to '11223344' + * 0x06 8 Always equal to 'deadbeefdeadface' + * 0x0e 16 Unknown + * + * The header can be prefixed by an optional, unknown-purpose byte. + */ + +static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, + const __u8 *data, unsigned int len) +{ + static const __u8 hdr[] = { + 0x11, 0x22, 0x33, 0x44, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xfa, 0xce + }; + + unsigned int maxlen, nbytes; + __u8 *mem; + int is_header = 0; + + if (buf == NULL) + return 0; + + if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || + (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { + uvc_trace(UVC_TRACE_FRAME, "iSight header found\n"); + is_header = 1; + } + + /* Synchronize to the input stream by waiting for a header packet. */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (!is_header) { + uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of " + "sync).\n"); + return 0; + } + + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. + */ + if (is_header && buf->buf.bytesused != 0) { + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + /* Copy the video data to the buffer. Skip header packets, as they + * contain no data. + */ + if (!is_header) { + maxlen = buf->buf.length - buf->buf.bytesused; + mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused; + nbytes = min(len, maxlen); + memcpy(mem, data, nbytes); + buf->buf.bytesused += nbytes; + + if (len > maxlen || buf->buf.bytesused == buf->buf.length) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete " + "(overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } + } + + return 0; +} + +void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf) +{ + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", + urb->iso_frame_desc[i].status); + } + + /* Decode the payload packet. + * uvc_video_decode is entered twice when a frame transition + * has been detected because the end of frame can only be + * reliably detected when the first packet of the new frame + * is processed. The first pass detects the transition and + * closes the previous frame's buffer, the second pass + * processes the data of the first payload of the new frame. + */ + do { + ret = isight_decode(&video->queue, buf, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + + if (buf == NULL) + break; + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + } +} diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c new file mode 100644 index 000000000000..0923f0e3b3d4 --- /dev/null +++ b/drivers/media/video/uvc/uvc_queue.c @@ -0,0 +1,477 @@ +/* + * uvc_queue.c -- USB Video Class driver - Buffers management + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * Video buffers queue management. + * + * Video queues is initialized by uvc_queue_init(). The function performs + * basic initialization of the uvc_video_queue struct and never fails. + * + * Video buffer allocation and freeing are performed by uvc_alloc_buffers and + * uvc_free_buffers respectively. The former acquires the video queue lock, + * while the later must be called with the lock held (so that allocation can + * free previously allocated buffers). Trying to free buffers that are mapped + * to user space will return -EBUSY. + * + * Video buffers are managed using two queues. However, unlike most USB video + * drivers which use an in queue and an out queue, we use a main queue which + * holds all queued buffers (both 'empty' and 'done' buffers), and an irq + * queue which holds empty buffers. This design (copied from video-buf) + * minimizes locking in interrupt, as only one queue is shared between + * interrupt and user contexts. + * + * Use cases + * --------- + * + * Unless stated otherwise, all operations which modify the irq buffers queue + * are protected by the irq spinlock. + * + * 1. The user queues the buffers, starts streaming and dequeues a buffer. + * + * The buffers are added to the main and irq queues. Both operations are + * protected by the queue lock, and the latert is protected by the irq + * spinlock as well. + * + * The completion handler fetches a buffer from the irq queue and fills it + * with video data. If no buffer is available (irq queue empty), the handler + * returns immediately. + * + * When the buffer is full, the completion handler removes it from the irq + * queue, marks it as ready (UVC_BUF_STATE_DONE) and wake its wait queue. + * At that point, any process waiting on the buffer will be woken up. If a + * process tries to dequeue a buffer after it has been marked ready, the + * dequeing will succeed immediately. + * + * 2. Buffers are queued, user is waiting on a buffer and the device gets + * disconnected. + * + * When the device is disconnected, the kernel calls the completion handler + * with an appropriate status code. The handler marks all buffers in the + * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so + * that any process waiting on a buffer gets woken up. + * + * Waking up up the first buffer on the irq list is not enough, as the + * process waiting on the buffer might restart the dequeue operation + * immediately. + * + */ + +void uvc_queue_init(struct uvc_video_queue *queue) +{ + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->mainqueue); + INIT_LIST_HEAD(&queue->irqqueue); +} + +/* + * Allocate the video buffers. + * + * Pages are reserved to make sure they will not be swaped, as they will be + * filled in URB completion handler. + * + * Buffers will be individually mapped, so they must all be page aligned. + */ +int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, + unsigned int buflength) +{ + unsigned int bufsize = PAGE_ALIGN(buflength); + unsigned int i; + void *mem = NULL; + int ret; + + if (nbuffers > UVC_MAX_VIDEO_BUFFERS) + nbuffers = UVC_MAX_VIDEO_BUFFERS; + + mutex_lock(&queue->mutex); + + if ((ret = uvc_free_buffers(queue)) < 0) + goto done; + + /* Bail out if no buffers should be allocated. */ + if (nbuffers == 0) + goto done; + + /* Decrement the number of buffers until allocation succeeds. */ + for (; nbuffers > 0; --nbuffers) { + mem = vmalloc_32(nbuffers * bufsize); + if (mem != NULL) + break; + } + + if (mem == NULL) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < nbuffers; ++i) { + memset(&queue->buffer[i], 0, sizeof queue->buffer[i]); + queue->buffer[i].buf.index = i; + queue->buffer[i].buf.m.offset = i * bufsize; + queue->buffer[i].buf.length = buflength; + queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->buffer[i].buf.sequence = 0; + queue->buffer[i].buf.field = V4L2_FIELD_NONE; + queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; + queue->buffer[i].buf.flags = 0; + init_waitqueue_head(&queue->buffer[i].wait); + } + + queue->mem = mem; + queue->count = nbuffers; + queue->buf_size = bufsize; + ret = nbuffers; + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Free the video buffers. + * + * This function must be called with the queue lock held. + */ +int uvc_free_buffers(struct uvc_video_queue *queue) +{ + unsigned int i; + + for (i = 0; i < queue->count; ++i) { + if (queue->buffer[i].vma_use_count != 0) + return -EBUSY; + } + + if (queue->count) { + vfree(queue->mem); + queue->count = 0; + } + + return 0; +} + +static void __uvc_query_buffer(struct uvc_buffer *buf, + struct v4l2_buffer *v4l2_buf) +{ + memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); + + if (buf->vma_use_count) + v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED; + + switch (buf->state) { + case UVC_BUF_STATE_ERROR: + case UVC_BUF_STATE_DONE: + v4l2_buf->flags |= V4L2_BUF_FLAG_DONE; + break; + case UVC_BUF_STATE_QUEUED: + case UVC_BUF_STATE_ACTIVE: + v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; + break; + case UVC_BUF_STATE_IDLE: + default: + break; + } +} + +int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf) +{ + int ret = 0; + + mutex_lock(&queue->mutex); + if (v4l2_buf->index >= queue->count) { + ret = -EINVAL; + goto done; + } + + __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Queue a video buffer. Attempting to queue a buffer that has already been + * queued will return -EINVAL. + */ +int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf) +{ + struct uvc_buffer *buf; + unsigned long flags; + int ret = 0; + + uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4l2_buf->memory != V4L2_MEMORY_MMAP) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " + "and/or memory (%u).\n", v4l2_buf->type, + v4l2_buf->memory); + return -EINVAL; + } + + mutex_lock(&queue->mutex); + if (v4l2_buf->index >= queue->count) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n"); + ret = -EINVAL; + goto done; + } + + buf = &queue->buffer[v4l2_buf->index]; + if (buf->state != UVC_BUF_STATE_IDLE) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state " + "(%u).\n", buf->state); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (queue->flags & UVC_QUEUE_DISCONNECTED) { + spin_unlock_irqrestore(&queue->irqlock, flags); + ret = -ENODEV; + goto done; + } + buf->state = UVC_BUF_STATE_QUEUED; + buf->buf.bytesused = 0; + list_add_tail(&buf->stream, &queue->mainqueue); + list_add_tail(&buf->queue, &queue->irqqueue); + spin_unlock_irqrestore(&queue->irqlock, flags); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking) +{ + if (nonblocking) { + return (buf->state != UVC_BUF_STATE_QUEUED && + buf->state != UVC_BUF_STATE_ACTIVE) + ? 0 : -EAGAIN; + } + + return wait_event_interruptible(buf->wait, + buf->state != UVC_BUF_STATE_QUEUED && + buf->state != UVC_BUF_STATE_ACTIVE); +} + +/* + * Dequeue a video buffer. If nonblocking is false, block until a buffer is + * available. + */ +int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking) +{ + struct uvc_buffer *buf; + int ret = 0; + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4l2_buf->memory != V4L2_MEMORY_MMAP) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " + "and/or memory (%u).\n", v4l2_buf->type, + v4l2_buf->memory); + return -EINVAL; + } + + mutex_lock(&queue->mutex); + if (list_empty(&queue->mainqueue)) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n"); + ret = -EINVAL; + goto done; + } + + buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); + if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0) + goto done; + + uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n", + buf->buf.index, buf->state, buf->buf.bytesused); + + switch (buf->state) { + case UVC_BUF_STATE_ERROR: + uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data " + "(transmission error).\n"); + ret = -EIO; + case UVC_BUF_STATE_DONE: + buf->state = UVC_BUF_STATE_IDLE; + break; + + case UVC_BUF_STATE_IDLE: + case UVC_BUF_STATE_QUEUED: + case UVC_BUF_STATE_ACTIVE: + default: + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u " + "(driver bug?).\n", buf->state); + ret = -EINVAL; + goto done; + } + + list_del(&buf->stream); + __uvc_query_buffer(buf, v4l2_buf); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Poll the video queue. + * + * This function implements video queue polling and is intended to be used by + * the device poll handler. + */ +unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait) +{ + struct uvc_buffer *buf; + unsigned int mask = 0; + + mutex_lock(&queue->mutex); + if (list_empty(&queue->mainqueue)) { + mask |= POLLERR; + goto done; + } + buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); + + poll_wait(file, &buf->wait, wait); + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + mask |= POLLIN | POLLRDNORM; + +done: + mutex_unlock(&queue->mutex); + return mask; +} + +/* + * Enable or disable the video buffers queue. + * + * The queue must be enabled before starting video acquisition and must be + * disabled after stopping it. This ensures that the video buffers queue + * state can be properly initialized before buffers are accessed from the + * interrupt handler. + * + * Enabling the video queue initializes parameters (such as sequence number, + * sync pattern, ...). If the queue is already enabled, return -EBUSY. + * + * Disabling the video queue cancels the queue and removes all buffers from + * the main queue. + * + * This function can't be called from interrupt context. Use + * uvc_queue_cancel() instead. + */ +int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +{ + unsigned int i; + int ret = 0; + + mutex_lock(&queue->mutex); + if (enable) { + if (uvc_queue_streaming(queue)) { + ret = -EBUSY; + goto done; + } + queue->sequence = 0; + queue->flags |= UVC_QUEUE_STREAMING; + } else { + uvc_queue_cancel(queue, 0); + INIT_LIST_HEAD(&queue->mainqueue); + + for (i = 0; i < queue->count; ++i) + queue->buffer[i].state = UVC_BUF_STATE_IDLE; + + queue->flags &= ~UVC_QUEUE_STREAMING; + } + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Cancel the video buffers queue. + * + * Cancelling the queue marks all buffers on the irq queue as erroneous, + * wakes them up and remove them from the queue. + * + * If the disconnect parameter is set, further calls to uvc_queue_buffer will + * fail with -ENODEV. + * + * This function acquires the irq spinlock and can be called from interrupt + * context. + */ +void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +{ + struct uvc_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + while (!list_empty(&queue->irqqueue)) { + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + list_del(&buf->queue); + buf->state = UVC_BUF_STATE_ERROR; + wake_up(&buf->wait); + } + /* This must be protected by the irqlock spinlock to avoid race + * conditions between uvc_queue_buffer and the disconnection event that + * could result in an interruptible wait in uvc_dequeue_buffer. Do not + * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED + * state outside the queue code. + */ + if (disconnect) + queue->flags |= UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) +{ + struct uvc_buffer *nextbuf; + unsigned long flags; + + if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && + buf->buf.length != buf->buf.bytesused) { + buf->state = UVC_BUF_STATE_QUEUED; + buf->buf.bytesused = 0; + return buf; + } + + spin_lock_irqsave(&queue->irqlock, flags); + list_del(&buf->queue); + if (!list_empty(&queue->irqqueue)) + nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + else + nextbuf = NULL; + spin_unlock_irqrestore(&queue->irqlock, flags); + + buf->buf.sequence = queue->sequence++; + do_gettimeofday(&buf->buf.timestamp); + + wake_up(&buf->wait); + return nextbuf; +} diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c new file mode 100644 index 000000000000..be9084e5eace --- /dev/null +++ b/drivers/media/video/uvc/uvc_status.c @@ -0,0 +1,207 @@ +/* + * uvc_status.c -- USB Video Class driver - Status endpoint + * + * Copyright (C) 2007-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/usb/input.h> + +#include "uvcvideo.h" + +/* -------------------------------------------------------------------------- + * Input device + */ +static int uvc_input_init(struct uvc_device *dev) +{ + struct usb_device *udev = dev->udev; + struct input_dev *input; + char *phys = NULL; + int ret; + + input = input_allocate_device(); + if (input == NULL) + return -ENOMEM; + + phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath), + GFP_KERNEL); + if (phys == NULL) { + ret = -ENOMEM; + goto error; + } + sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath); + + input->name = dev->name; + input->phys = phys; + usb_to_input_id(udev, &input->id); + input->dev.parent = &dev->intf->dev; + + set_bit(EV_KEY, input->evbit); + set_bit(BTN_0, input->keybit); + + if ((ret = input_register_device(input)) < 0) + goto error; + + dev->input = input; + return 0; + +error: + input_free_device(input); + kfree(phys); + return ret; +} + +static void uvc_input_cleanup(struct uvc_device *dev) +{ + if (dev->input) + input_unregister_device(dev->input); +} + +/* -------------------------------------------------------------------------- + * Status interrupt endpoint + */ +static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) +{ + if (len < 3) { + uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " + "received.\n"); + return; + } + + if (data[2] == 0) { + if (len < 4) + return; + uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", + data[1], data[3] ? "pressed" : "released", len); + if (dev->input) + input_report_key(dev->input, BTN_0, data[3]); + } else { + uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " + "len %d.\n", data[1], data[2], data[3], len); + } +} + +static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) +{ + char *attrs[3] = { "value", "info", "failure" }; + + if (len < 6 || data[2] != 0 || data[4] > 2) { + uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " + "received.\n"); + return; + } + + uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", + data[1], data[3], attrs[data[4]], len); +} + +static void uvc_status_complete(struct urb *urb) +{ + struct uvc_device *dev = urb->context; + int len, ret; + + switch (urb->status) { + case 0: + break; + + case -ENOENT: /* usb_kill_urb() called. */ + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + case -EPROTO: /* Device is disconnected (reported by some + * host controller). */ + return; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " + "completion handler.\n", urb->status); + return; + } + + len = urb->actual_length; + if (len > 0) { + switch (dev->status[0] & 0x0f) { + case UVC_STATUS_TYPE_CONTROL: + uvc_event_control(dev, dev->status, len); + break; + + case UVC_STATUS_TYPE_STREAMING: + uvc_event_streaming(dev, dev->status, len); + break; + + default: + uvc_printk(KERN_INFO, "unknown event type %u.\n", + dev->status[0]); + break; + } + } + + /* Resubmit the URB. */ + urb->interval = dev->int_ep->desc.bInterval; + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); + } +} + +int uvc_status_init(struct uvc_device *dev) +{ + struct usb_host_endpoint *ep = dev->int_ep; + unsigned int pipe; + int interval; + + if (ep == NULL) + return 0; + + uvc_input_init(dev); + + dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (dev->int_urb == NULL) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); + + /* For high-speed interrupt endpoints, the bInterval value is used as + * an exponent of two. Some developers forgot about it. + */ + interval = ep->desc.bInterval; + if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && + (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) + interval = fls(interval) - 1; + + usb_fill_int_urb(dev->int_urb, dev->udev, pipe, + dev->status, sizeof dev->status, uvc_status_complete, + dev, interval); + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} + +void uvc_status_cleanup(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + usb_free_urb(dev->int_urb); + uvc_input_cleanup(dev); +} + +int uvc_status_suspend(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + return 0; +} + +int uvc_status_resume(struct uvc_device *dev) +{ + if (dev->int_urb == NULL) + return 0; + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c new file mode 100644 index 000000000000..2e0a66575bb4 --- /dev/null +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -0,0 +1,1105 @@ +/* + * uvc_v4l2.c -- USB Video Class driver - V4L2 API + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/wait.h> +#include <asm/atomic.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * V4L2 interface + */ + +/* + * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Most of the UVC controls exist in V4L2, and can be mapped directly. Some + * must be grouped (for instance the Red Balance, Blue Balance and Do White + * Balance V4L2 controls use the White Balance Component UVC control) or + * otherwise translated. The approach we take here is to use a translation + * table for the controls which can be mapped directly, and handle the others + * manually. + */ +static int uvc_v4l2_query_menu(struct uvc_video_device *video, + struct v4l2_querymenu *query_menu) +{ + struct uvc_menu_info *menu_info; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + + ctrl = uvc_find_control(video, query_menu->id, &mapping); + if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) + return -EINVAL; + + if (query_menu->index >= mapping->menu_count) + return -EINVAL; + + menu_info = &mapping->menu_info[query_menu->index]; + strncpy(query_menu->name, menu_info->name, 32); + return 0; +} + +/* + * Find the frame interval closest to the requested frame interval for the + * given frame format and size. This should be done by the device as part of + * the Video Probe and Commit negotiation, but some hardware don't implement + * that feature. + */ +static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) +{ + unsigned int i; + + if (frame->bFrameIntervalType) { + __u32 best = -1, dist; + + for (i = 0; i < frame->bFrameIntervalType; ++i) { + dist = interval > frame->dwFrameInterval[i] + ? interval - frame->dwFrameInterval[i] + : frame->dwFrameInterval[i] - interval; + + if (dist > best) + break; + + best = dist; + } + + interval = frame->dwFrameInterval[i-1]; + } else { + const __u32 min = frame->dwFrameInterval[0]; + const __u32 max = frame->dwFrameInterval[1]; + const __u32 step = frame->dwFrameInterval[2]; + + interval = min + (interval - min + step/2) / step * step; + if (interval > max) + interval = max; + } + + return interval; +} + +static int uvc_v4l2_try_format(struct uvc_video_device *video, + struct v4l2_format *fmt, struct uvc_streaming_control *probe, + struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) +{ + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + __u16 rw, rh; + unsigned int d, maxd; + unsigned int i; + __u32 interval; + int ret = 0; + __u8 *fcc; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fcc = (__u8 *)&fmt->fmt.pix.pixelformat; + uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); + + /* Check if the hardware supports the requested format. */ + for (i = 0; i < video->streaming->nformats; ++i) { + format = &video->streaming->format[i]; + if (format->fcc == fmt->fmt.pix.pixelformat) + break; + } + + if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n", + fmt->fmt.pix.pixelformat); + return -EINVAL; + } + + /* Find the closest image size. The distance between image sizes is + * the size in pixels of the non-overlapping regions between the + * requested size and the frame-specified size. + */ + rw = fmt->fmt.pix.width; + rh = fmt->fmt.pix.height; + maxd = (unsigned int)-1; + + for (i = 0; i < format->nframes; ++i) { + __u16 w = format->frame[i].wWidth; + __u16 h = format->frame[i].wHeight; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + frame = &format->frame[i]; + } + + if (maxd == 0) + break; + } + + if (frame == NULL) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", + fmt->fmt.pix.width, fmt->fmt.pix.height); + return -EINVAL; + } + + /* Use the default frame interval. */ + interval = frame->dwDefaultFrameInterval; + uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " + "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, + (100000000/interval)%10); + + /* Set the format index, frame index and frame interval. */ + memset(probe, 0, sizeof *probe); + probe->bmHint = 1; /* dwFrameInterval */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); + /* Some webcams stall the probe control set request when the + * dwMaxVideoFrameSize field is set to zero. The UVC specification + * clearly states that the field is read-only from the host, so this + * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by + * the webcam to work around the problem. + * + * The workaround could probably be enabled for all webcams, so the + * quirk can be removed if needed. It's currently useful to detect + * webcam bugs and fix them before they hit the market (providing + * developers test their webcams with the Linux driver as well as with + * the Windows driver). + */ + if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + probe->dwMaxVideoFrameSize = + video->streaming->ctrl.dwMaxVideoFrameSize; + + /* Probe the device */ + if ((ret = uvc_probe_video(video, probe)) < 0) + goto done; + + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + if (uvc_format != NULL) + *uvc_format = format; + if (uvc_frame != NULL) + *uvc_frame = frame; + +done: + return ret; +} + +static int uvc_v4l2_get_format(struct uvc_video_device *video, + struct v4l2_format *fmt) +{ + struct uvc_format *format = video->streaming->cur_format; + struct uvc_frame *frame = video->streaming->cur_frame; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (format == NULL || frame == NULL) + return -EINVAL; + + fmt->fmt.pix.pixelformat = format->fcc; + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int uvc_v4l2_set_format(struct uvc_video_device *video, + struct v4l2_format *fmt) +{ + struct uvc_streaming_control probe; + struct uvc_format *format; + struct uvc_frame *frame; + int ret; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (uvc_queue_streaming(&video->queue)) + return -EBUSY; + + ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); + if (ret < 0) + return ret; + + if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + return ret; + + memcpy(&video->streaming->ctrl, &probe, sizeof probe); + video->streaming->cur_format = format; + video->streaming->cur_frame = frame; + + return 0; +} + +static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, + struct v4l2_streamparm *parm) +{ + uint32_t numerator, denominator; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + numerator = video->streaming->ctrl.dwFrameInterval; + denominator = 10000000; + uvc_simplify_fraction(&numerator, &denominator, 8, 333); + + memset(parm, 0, sizeof *parm); + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.capturemode = 0; + parm->parm.capture.timeperframe.numerator = numerator; + parm->parm.capture.timeperframe.denominator = denominator; + parm->parm.capture.extendedmode = 0; + parm->parm.capture.readbuffers = 0; + + return 0; +} + +static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, + struct v4l2_streamparm *parm) +{ + struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_streaming_control probe; + uint32_t interval; + int ret; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (uvc_queue_streaming(&video->queue)) + return -EBUSY; + + memcpy(&probe, &video->streaming->ctrl, sizeof probe); + interval = uvc_fraction_to_interval( + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator); + + uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator, + interval); + probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); + + /* Probe the device with the new settings. */ + if ((ret = uvc_probe_video(video, &probe)) < 0) + return ret; + + /* Commit the new settings. */ + if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + return ret; + + memcpy(&video->streaming->ctrl, &probe, sizeof probe); + + /* Return the actual frame period. */ + parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval; + parm->parm.capture.timeperframe.denominator = 10000000; + uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator, + &parm->parm.capture.timeperframe.denominator, + 8, 333); + + return 0; +} + +/* ------------------------------------------------------------------------ + * Privilege management + */ + +/* + * Privilege management is the multiple-open implementation basis. The current + * implementation is completely transparent for the end-user and doesn't + * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. + * Those ioctls enable finer control on the device (by making possible for a + * user to request exclusive access to a device), but are not mature yet. + * Switching to the V4L2 priority mechanism might be considered in the future + * if this situation changes. + * + * Each open instance of a UVC device can either be in a privileged or + * unprivileged state. Only a single instance can be in a privileged state at + * a given time. Trying to perform an operation which requires privileges will + * automatically acquire the required privileges if possible, or return -EBUSY + * otherwise. Privileges are dismissed when closing the instance. + * + * Operations which require privileges are: + * + * - VIDIOC_S_INPUT + * - VIDIOC_S_PARM + * - VIDIOC_S_FMT + * - VIDIOC_TRY_FMT + * - VIDIOC_REQBUFS + */ +static int uvc_acquire_privileges(struct uvc_fh *handle) +{ + int ret = 0; + + /* Always succeed if the handle is already privileged. */ + if (handle->state == UVC_HANDLE_ACTIVE) + return 0; + + /* Check if the device already has a privileged handle. */ + mutex_lock(&uvc_driver.open_mutex); + if (atomic_inc_return(&handle->device->active) != 1) { + atomic_dec(&handle->device->active); + ret = -EBUSY; + goto done; + } + + handle->state = UVC_HANDLE_ACTIVE; + +done: + mutex_unlock(&uvc_driver.open_mutex); + return ret; +} + +static void uvc_dismiss_privileges(struct uvc_fh *handle) +{ + if (handle->state == UVC_HANDLE_ACTIVE) + atomic_dec(&handle->device->active); + + handle->state = UVC_HANDLE_PASSIVE; +} + +static int uvc_has_privileges(struct uvc_fh *handle) +{ + return handle->state == UVC_HANDLE_ACTIVE; +} + +/* ------------------------------------------------------------------------ + * V4L2 file operations + */ + +static int uvc_v4l2_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev; + struct uvc_video_device *video; + struct uvc_fh *handle; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); + mutex_lock(&uvc_driver.open_mutex); + vdev = video_devdata(file); + video = video_get_drvdata(vdev); + + if (video->dev->state & UVC_DEV_DISCONNECTED) { + ret = -ENODEV; + goto done; + } + + ret = usb_autopm_get_interface(video->dev->intf); + if (ret < 0) + goto done; + + /* Create the device handle. */ + handle = kzalloc(sizeof *handle, GFP_KERNEL); + if (handle == NULL) { + usb_autopm_put_interface(video->dev->intf); + ret = -ENOMEM; + goto done; + } + + handle->device = video; + handle->state = UVC_HANDLE_PASSIVE; + file->private_data = handle; + + kref_get(&video->dev->kref); + +done: + mutex_unlock(&uvc_driver.open_mutex); + return ret; +} + +static int uvc_v4l2_release(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); + + /* Only free resources if this is a privileged handle. */ + if (uvc_has_privileges(handle)) { + uvc_video_enable(video, 0); + + mutex_lock(&video->queue.mutex); + if (uvc_free_buffers(&video->queue) < 0) + uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to " + "free buffers.\n"); + mutex_unlock(&video->queue.mutex); + } + + /* Release the file handle. */ + uvc_dismiss_privileges(handle); + kfree(handle); + file->private_data = NULL; + + usb_autopm_put_interface(video->dev->intf); + kref_put(&video->dev->kref, uvc_delete); + return 0; +} + +static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + int ret = 0; + + if (uvc_trace_param & UVC_TRACE_IOCTL) + v4l_printk_ioctl(cmd); + + switch (cmd) { + /* Query capabilities */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof *cap); + strncpy(cap->driver, "uvcvideo", sizeof cap->driver); + strncpy(cap->card, vdev->name, 32); + strncpy(cap->bus_info, video->dev->udev->bus->bus_name, + sizeof cap->bus_info); + cap->version = DRIVER_VERSION_NUMBER; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING; + break; + } + + /* Get, Set & Query control */ + case VIDIOC_QUERYCTRL: + return uvc_query_v4l2_ctrl(video, arg); + + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + + uvc_ctrl_begin(video); + ret = uvc_ctrl_get(video, &xctrl); + uvc_ctrl_rollback(video); + if (ret >= 0) + ctrl->value = xctrl.value; + break; + } + + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + xctrl.value = ctrl->value; + + uvc_ctrl_begin(video); + ret = uvc_ctrl_set(video, &xctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + return ret; + } + ret = uvc_ctrl_commit(video); + break; + } + + case VIDIOC_QUERYMENU: + return uvc_v4l2_query_menu(video, arg); + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + uvc_ctrl_begin(video); + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_get(video, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + ctrls->error_idx = i; + return ret; + } + } + ctrls->error_idx = 0; + ret = uvc_ctrl_rollback(video); + break; + } + + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + ret = uvc_ctrl_begin(video); + if (ret < 0) + return ret; + + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_set(video, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + ctrls->error_idx = i; + return ret; + } + } + + ctrls->error_idx = 0; + + if (cmd == VIDIOC_S_EXT_CTRLS) + ret = uvc_ctrl_commit(video); + else + ret = uvc_ctrl_rollback(video); + break; + } + + /* Get, Set & Enum input */ + case VIDIOC_ENUMINPUT: + { + const struct uvc_entity *selector = video->selector; + struct v4l2_input *input = arg; + struct uvc_entity *iterm = NULL; + u32 index = input->index; + int pin = 0; + + if (selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (index != 0) + return -EINVAL; + iterm = list_first_entry(&video->iterms, + struct uvc_entity, chain); + pin = iterm->id; + } else if (pin < selector->selector.bNrInPins) { + pin = selector->selector.baSourceID[index]; + list_for_each_entry(iterm, video->iterms.next, chain) { + if (iterm->id == pin) + break; + } + } + + if (iterm == NULL || iterm->id != pin) + return -EINVAL; + + memset(input, 0, sizeof *input); + input->index = index; + strncpy(input->name, iterm->name, sizeof input->name); + if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) + input->type = V4L2_INPUT_TYPE_CAMERA; + break; + } + + case VIDIOC_G_INPUT: + { + u8 input; + + if (video->selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + *(int *)arg = 0; + break; + } + + ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id, + video->dev->intfnum, SU_INPUT_SELECT_CONTROL, + &input, 1); + if (ret < 0) + return ret; + + *(int *)arg = input - 1; + break; + } + + case VIDIOC_S_INPUT: + { + u8 input = *(u32 *)arg + 1; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + if (video->selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (input != 1) + return -EINVAL; + break; + } + + if (input > video->selector->selector.bNrInPins) + return -EINVAL; + + return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, + video->dev->intfnum, SU_INPUT_SELECT_CONTROL, + &input, 1); + } + + /* Try, Get, Set & Enum format */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + struct uvc_format *format; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fmt->index >= video->streaming->nformats) + return -EINVAL; + + format = &video->streaming->format[fmt->index]; + fmt->flags = 0; + if (format->flags & UVC_FMT_FLAG_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + strncpy(fmt->description, format->name, + sizeof fmt->description); + fmt->description[sizeof fmt->description - 1] = 0; + fmt->pixelformat = format->fcc; + break; + } + + case VIDIOC_TRY_FMT: + { + struct uvc_streaming_control probe; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL); + } + + case VIDIOC_S_FMT: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_format(video, arg); + + case VIDIOC_G_FMT: + return uvc_v4l2_get_format(video, arg); + + /* Frame size enumeration */ + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame; + int i; + + /* Look for the given pixel format */ + for (i = 0; i < video->streaming->nformats; i++) { + if (video->streaming->format[i].fcc == + fsize->pixel_format) { + format = &video->streaming->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + if (fsize->index >= format->nframes) + return -EINVAL; + + frame = &format->frame[fsize->index]; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = frame->wWidth; + fsize->discrete.height = frame->wHeight; + break; + } + + /* Frame interval enumeration */ + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + int i; + + /* Look for the given pixel format and frame size */ + for (i = 0; i < video->streaming->nformats; i++) { + if (video->streaming->format[i].fcc == + fival->pixel_format) { + format = &video->streaming->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + for (i = 0; i < format->nframes; i++) { + if (format->frame[i].wWidth == fival->width && + format->frame[i].wHeight == fival->height) { + frame = &format->frame[i]; + break; + } + } + if (frame == NULL) + return -EINVAL; + + if (frame->bFrameIntervalType) { + if (fival->index >= frame->bFrameIntervalType) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = + frame->dwFrameInterval[fival->index]; + fival->discrete.denominator = 10000000; + uvc_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + } else { + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + fival->stepwise.min.numerator = + frame->dwFrameInterval[0]; + fival->stepwise.min.denominator = 10000000; + fival->stepwise.max.numerator = + frame->dwFrameInterval[1]; + fival->stepwise.max.denominator = 10000000; + fival->stepwise.step.numerator = + frame->dwFrameInterval[2]; + fival->stepwise.step.denominator = 10000000; + uvc_simplify_fraction(&fival->stepwise.min.numerator, + &fival->stepwise.min.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.max.numerator, + &fival->stepwise.max.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.step.numerator, + &fival->stepwise.step.denominator, 8, 333); + } + break; + } + + /* Get & Set streaming parameters */ + case VIDIOC_G_PARM: + return uvc_v4l2_get_streamparm(video, arg); + + case VIDIOC_S_PARM: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_streamparm(video, arg); + + /* Cropping and scaling */ + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *ccap = arg; + struct uvc_frame *frame = video->streaming->cur_frame; + + if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + ccap->bounds.left = 0; + ccap->bounds.top = 0; + ccap->bounds.width = frame->wWidth; + ccap->bounds.height = frame->wHeight; + + ccap->defrect = ccap->bounds; + + ccap->pixelaspect.numerator = 1; + ccap->pixelaspect.denominator = 1; + break; + } + + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + return -EINVAL; + + /* Buffers & streaming */ + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *rb = arg; + unsigned int bufsize = + video->streaming->ctrl.dwMaxVideoFrameSize; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize); + if (ret < 0) + return ret; + + if (!(video->streaming->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; + + rb->count = ret; + ret = 0; + break; + } + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_query_buffer(&video->queue, buf); + } + + case VIDIOC_QBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_queue_buffer(&video->queue, arg); + + case VIDIOC_DQBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_dequeue_buffer(&video->queue, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + { + int *type = arg; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + if ((ret = uvc_video_enable(video, 1)) < 0) + return ret; + break; + } + + case VIDIOC_STREAMOFF: + { + int *type = arg; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_video_enable(video, 0); + } + + /* Analog video standards make no sense for digital cameras. */ + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + + case VIDIOC_OVERLAY: + + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + + case VIDIOC_ENUMOUTPUT: + uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); + return -EINVAL; + + /* Dynamic controls. */ + case UVCIOC_CTRL_ADD: + { + struct uvc_xu_control_info *xinfo = arg; + struct uvc_control_info *info; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + info = kmalloc(sizeof *info, GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + + memcpy(info->entity, xinfo->entity, sizeof info->entity); + info->index = xinfo->index; + info->selector = xinfo->selector; + info->size = xinfo->size; + info->flags = xinfo->flags; + + info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | + UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; + + ret = uvc_ctrl_add_info(info); + if (ret < 0) + kfree(info); + break; + } + + case UVCIOC_CTRL_MAP: + { + struct uvc_xu_control_mapping *xmap = arg; + struct uvc_control_mapping *map; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + map = kmalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + ret = uvc_ctrl_add_mapping(map); + if (ret < 0) + kfree(map); + break; + } + + case UVCIOC_CTRL_GET: + return uvc_xu_ctrl_query(video, arg, 0); + + case UVCIOC_CTRL_SET: + return uvc_xu_ctrl_query(video, arg, 1); + + default: + if ((ret = v4l_compat_translate_ioctl(inode, file, cmd, arg, + uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) + uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", + cmd); + return ret; + } + + return ret; +} + +static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); + return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl); +} + +static ssize_t uvc_v4l2_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); + return -ENODEV; +} + +/* + * VMA operations. + */ +static void uvc_vm_open(struct vm_area_struct *vma) +{ + struct uvc_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count++; +} + +static void uvc_vm_close(struct vm_area_struct *vma) +{ + struct uvc_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count--; +} + +static struct vm_operations_struct uvc_vm_ops = { + .open = uvc_vm_open, + .close = uvc_vm_close, +}; + +static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_buffer *buffer; + struct page *page; + unsigned long addr, start, size; + unsigned int i; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); + + start = vma->vm_start; + size = vma->vm_end - vma->vm_start; + + mutex_lock(&video->queue.mutex); + + for (i = 0; i < video->queue.count; ++i) { + buffer = &video->queue.buffer[i]; + if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + + if (i == video->queue.count || size != video->queue.buf_size) { + ret = -EINVAL; + goto done; + } + + /* + * VM_IO marks the area as being an mmaped region for I/O to a + * device. It also prevents the region from being core dumped. + */ + vma->vm_flags |= VM_IO; + + addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; + while (size > 0) { + page = vmalloc_to_page((void *)addr); + if ((ret = vm_insert_page(vma, start, page)) < 0) + goto done; + + start += PAGE_SIZE; + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &uvc_vm_ops; + vma->vm_private_data = buffer; + uvc_vm_open(vma); + +done: + mutex_unlock(&video->queue.mutex); + return ret; +} + +static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); + + return uvc_queue_poll(&video->queue, file, wait); +} + +struct file_operations uvc_fops = { + .owner = THIS_MODULE, + .open = uvc_v4l2_open, + .release = uvc_v4l2_release, + .ioctl = uvc_v4l2_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, + .read = uvc_v4l2_read, + .mmap = uvc_v4l2_mmap, + .poll = uvc_v4l2_poll, +}; diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c new file mode 100644 index 000000000000..6faf1fb21614 --- /dev/null +++ b/drivers/media/video/uvc/uvc_video.c @@ -0,0 +1,934 @@ +/* + * uvc_video.c -- USB Video Class driver - Video handling + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include <asm/unaligned.h> + +#include <media/v4l2-common.h> + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size, + int timeout) +{ + __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + unsigned int pipe; + int ret; + + pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) + : usb_sndctrlpipe(dev->udev, 0); + type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; + + ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8, + unit << 8 | intfnum, data, size, timeout); + + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " + "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, + size); + return -EIO; + } + + return 0; +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, + UVC_CTRL_CONTROL_TIMEOUT); +} + +static void uvc_fixup_buffer_size(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl) +{ + struct uvc_format *format; + struct uvc_frame *frame; + + if (ctrl->bFormatIndex <= 0 || + ctrl->bFormatIndex > video->streaming->nformats) + return; + + format = &video->streaming->format[ctrl->bFormatIndex - 1]; + + if (ctrl->bFrameIndex <= 0 || + ctrl->bFrameIndex > format->nframes) + return; + + frame = &format->frame[ctrl->bFrameIndex - 1]; + + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || + (ctrl->dwMaxVideoFrameSize == 0 && + video->dev->uvc_version < 0x0110)) + ctrl->dwMaxVideoFrameSize = + frame->dwMaxVideoFrameBufferSize; +} + +static int uvc_get_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe, __u8 query) +{ + __u8 data[34]; + __u8 size; + int ret; + + size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, + probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size, + UVC_CTRL_STREAMING_TIMEOUT); + + if (ret < 0) + return ret; + + ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); + ctrl->bFormatIndex = data[2]; + ctrl->bFrameIndex = data[3]; + ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]); + ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]); + ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]); + ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); + ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); + ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); + ctrl->dwMaxVideoFrameSize = + le32_to_cpu(get_unaligned((__le32 *)&data[18])); + ctrl->dwMaxPayloadTransferSize = + le32_to_cpu(get_unaligned((__le32 *)&data[22])); + + if (size == 34) { + ctrl->dwClockFrequency = + le32_to_cpu(get_unaligned((__le32 *)&data[26])); + ctrl->bmFramingInfo = data[30]; + ctrl->bPreferedVersion = data[31]; + ctrl->bMinVersion = data[32]; + ctrl->bMaxVersion = data[33]; + } else { + ctrl->dwClockFrequency = video->dev->clock_frequency; + ctrl->bmFramingInfo = 0; + ctrl->bPreferedVersion = 0; + ctrl->bMinVersion = 0; + ctrl->bMaxVersion = 0; + } + + /* Some broken devices return a null or wrong dwMaxVideoFrameSize. + * Try to get the value from the format and frame descriptor. + */ + uvc_fixup_buffer_size(video, ctrl); + + return 0; +} + +int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe) +{ + __u8 data[34]; + __u8 size; + + size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + memset(data, 0, sizeof data); + + *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint); + data[2] = ctrl->bFormatIndex; + data[3] = ctrl->bFrameIndex; + *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval); + *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate); + *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate); + *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality); + *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize); + *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay); + /* Note: Some of the fields below are not required for IN devices (see + * UVC spec, 4.3.1.1), but we still copy them in case support for OUT + * devices is added in the future. */ + put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize), + (__le32 *)&data[18]); + put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize), + (__le32 *)&data[22]); + + if (size == 34) { + put_unaligned(cpu_to_le32(ctrl->dwClockFrequency), + (__le32 *)&data[26]); + data[30] = ctrl->bmFramingInfo; + data[31] = ctrl->bPreferedVersion; + data[32] = ctrl->bMinVersion; + data[33] = ctrl->bMaxVersion; + } + + return __uvc_query_ctrl(video->dev, SET_CUR, 0, + video->streaming->intfnum, + probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size, + UVC_CTRL_STREAMING_TIMEOUT); +} + +int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe) +{ + struct uvc_streaming_control probe_min, probe_max; + __u16 bandwidth; + unsigned int i; + int ret; + + mutex_lock(&video->streaming->mutex); + + /* Perform probing. The device should adjust the requested values + * according to its capabilities. However, some devices, namely the + * first generation UVC Logitech webcams, don't implement the Video + * Probe control properly, and just return the needed bandwidth. For + * that reason, if the needed bandwidth exceeds the maximum available + * bandwidth, try to lower the quality. + */ + if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0) + goto done; + + /* Get the minimum and maximum values for compression settings. */ + if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX); + if (ret < 0) + goto done; + + probe->wCompQuality = probe_max.wCompQuality; + } + + for (i = 0; i < 2; ++i) { + if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 || + (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + goto done; + + if (video->streaming->intf->num_altsetting == 1) + break; + + bandwidth = probe->dwMaxPayloadTransferSize; + if (bandwidth <= video->streaming->maxpsize) + break; + + if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + ret = -ENOSPC; + goto done; + } + + /* TODO: negotiate compression parameters */ + probe->wKeyFrameRate = probe_min.wKeyFrameRate; + probe->wPFrameRate = probe_min.wPFrameRate; + probe->wCompQuality = probe_max.wCompQuality; + probe->wCompWindowSize = probe_min.wCompWindowSize; + } + +done: + mutex_unlock(&video->streaming->mutex); + return ret; +} + +/* ------------------------------------------------------------------------ + * Video codecs + */ + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +/* Video payload decoding is handled by uvc_video_decode_start(), + * uvc_video_decode_data() and uvc_video_decode_end(). + * + * uvc_video_decode_start is called with URB data at the start of a bulk or + * isochronous payload. It processes header data and returns the header size + * in bytes if successful. If an error occurs, it returns a negative error + * code. The following error codes have special meanings. + * + * - EAGAIN informs the caller that the current video buffer should be marked + * as done, and that the function should be called again with the same data + * and a new video buffer. This is used when end of frame conditions can be + * reliably detected at the beginning of the next frame only. + * + * If an error other than -EAGAIN is returned, the caller will drop the current + * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be + * made until the next payload. -ENODATA can be used to drop the current + * payload if no other error code is appropriate. + * + * uvc_video_decode_data is called for every URB with URB data. It copies the + * data to the video buffer. + * + * uvc_video_decode_end is called with header data at the end of a bulk or + * isochronous payload. It performs any additional header data processing and + * returns 0 or a negative error code if an error occured. As header data have + * already been processed by uvc_video_decode_start, this functions isn't + * required to perform sanity checks a second time. + * + * For isochronous transfers where a payload is always transfered in a single + * URB, the three functions will be called in a row. + * + * To let the decoder process header data and update its internal state even + * when no video buffer is available, uvc_video_decode_start must be prepared + * to be called with a NULL buf parameter. uvc_video_decode_data and + * uvc_video_decode_end will never be called with a NULL buffer. + */ +static int uvc_video_decode_start(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + __u8 fid; + + /* Sanity checks: + * - packet must be at least 2 bytes long + * - bHeaderLength value must be at least 2 bytes (see above) + * - bHeaderLength value can't be larger than the packet size. + */ + if (len < 2 || data[0] < 2 || data[0] > len) + return -EINVAL; + + /* Skip payloads marked with the error bit ("error frames"). */ + if (data[1] & UVC_STREAM_ERR) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit " + "set).\n"); + return -ENODATA; + } + + fid = data[1] & UVC_STREAM_FID; + + /* Store the payload FID bit and return immediately when the buffer is + * NULL. + */ + if (buf == NULL) { + video->last_fid = fid; + return -ENODATA; + } + + /* Synchronize to the input stream by waiting for the FID bit to be + * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. + * queue->last_fid is initialized to -1, so the first isochronous + * frame will always be in sync. + * + * If the device doesn't toggle the FID bit, invert video->last_fid + * when the EOF bit is set to force synchronisation on the next packet. + */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (fid == video->last_fid) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " + "sync).\n"); + if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + (data[1] & UVC_STREAM_EOF)) + video->last_fid ^= UVC_STREAM_FID; + return -ENODATA; + } + + /* TODO: Handle PTS and SCR. */ + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * queue->last_fid is initialized to -1, so the first isochronous + * frame will never trigger an end of frame detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting and of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != video->last_fid && buf->buf.bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " + "toggled).\n"); + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + video->last_fid = fid; + + return data[0]; +} + +static void uvc_video_decode_data(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + struct uvc_video_queue *queue = &video->queue; + unsigned int maxlen, nbytes; + void *mem; + + if (len <= 0) + return; + + /* Copy the video data to the buffer. */ + maxlen = buf->buf.length - buf->buf.bytesused; + mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused; + nbytes = min((unsigned int)len, maxlen); + memcpy(mem, data, nbytes); + buf->buf.bytesused += nbytes; + + /* Complete the current frame if the buffer size was exceeded. */ + if (len > maxlen) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } +} + +static void uvc_video_decode_end(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + /* Mark the buffer as done if the EOF marker is set. */ + if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); + if (data[0] == len) + uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); + buf->state = UVC_BUF_STATE_DONE; + if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + video->last_fid ^= UVC_STREAM_FID; + } +} + +/* ------------------------------------------------------------------------ + * URB handling + */ + +/* + * Completion handler for video URBs. + */ +static void uvc_video_decode_isoc(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem; + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", urb->iso_frame_desc[i].status); + continue; + } + + /* Decode the payload header. */ + mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + do { + ret = uvc_video_decode_start(video, buf, mem, + urb->iso_frame_desc[i].actual_length); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + + if (ret < 0) + continue; + + /* Decode the payload data. */ + uvc_video_decode_data(video, buf, mem + ret, + urb->iso_frame_desc[i].actual_length - ret); + + /* Process the header again. */ + uvc_video_decode_end(video, buf, mem, ret); + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } +} + +static void uvc_video_decode_bulk(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem; + int len, ret; + + mem = urb->transfer_buffer; + len = urb->actual_length; + video->bulk.payload_size += len; + + /* If the URB is the first of its payload, decode and save the + * header. + */ + if (video->bulk.header_size == 0) { + do { + ret = uvc_video_decode_start(video, buf, mem, len); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + + /* If an error occured skip the rest of the payload. */ + if (ret < 0 || buf == NULL) { + video->bulk.skip_payload = 1; + return; + } + + video->bulk.header_size = ret; + memcpy(video->bulk.header, mem, video->bulk.header_size); + + mem += ret; + len -= ret; + } + + /* The buffer queue might have been cancelled while a bulk transfer + * was in progress, so we can reach here with buf equal to NULL. Make + * sure buf is never dereferenced if NULL. + */ + + /* Process video data. */ + if (!video->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(video, buf, mem, len); + + /* Detect the payload end by a URB smaller than the maximum size (or + * a payload size equal to the maximum) and process the header again. + */ + if (urb->actual_length < urb->transfer_buffer_length || + video->bulk.payload_size >= video->bulk.max_payload_size) { + if (!video->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(video, buf, video->bulk.header, + video->bulk.header_size); + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } + + video->bulk.header_size = 0; + video->bulk.skip_payload = 0; + video->bulk.payload_size = 0; + } +} + +static void uvc_video_complete(struct urb *urb) +{ + struct uvc_video_device *video = urb->context; + struct uvc_video_queue *queue = &video->queue; + struct uvc_buffer *buf = NULL; + unsigned long flags; + int ret; + + switch (urb->status) { + case 0: + break; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " + "completion handler.\n", urb->status); + + case -ENOENT: /* usb_kill_urb() called. */ + if (video->frozen) + return; + + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); + return; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (!list_empty(&queue->irqqueue)) + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + spin_unlock_irqrestore(&queue->irqlock, flags); + + video->decode(urb, video, buf); + + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", + ret); + } +} + +/* + * Uninitialize isochronous/bulk URBs and free transfer buffers. + */ +static void uvc_uninit_video(struct uvc_video_device *video) +{ + struct urb *urb; + unsigned int i; + + for (i = 0; i < UVC_URBS; ++i) { + if ((urb = video->urb[i]) == NULL) + continue; + + usb_kill_urb(urb); + /* urb->transfer_buffer_length is not touched by USB core, so + * we can use it here as the buffer length. + */ + if (video->urb_buffer[i]) { + usb_buffer_free(video->dev->udev, + urb->transfer_buffer_length, + video->urb_buffer[i], urb->transfer_dma); + video->urb_buffer[i] = NULL; + } + + usb_free_urb(urb); + video->urb[i] = NULL; + } +} + +/* + * Initialize isochronous URBs and allocate transfer buffers. The packet size + * is given by the endpoint. + */ +static int uvc_init_video_isoc(struct uvc_video_device *video, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + unsigned int npackets, i, j; + __u16 psize; + __u32 size; + + /* Compute the number of isochronous packets to allocate by dividing + * the maximum video frame size by the packet size. Limit the result + * to UVC_MAX_ISO_PACKETS. + */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + + size = video->streaming->ctrl.dwMaxVideoFrameSize; + if (size > UVC_MAX_FRAME_SIZE) + return -EINVAL; + + npackets = (size + psize - 1) / psize; + if (npackets > UVC_MAX_ISO_PACKETS) + npackets = UVC_MAX_ISO_PACKETS; + + size = npackets * psize; + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(npackets, GFP_KERNEL); + if (urb == NULL) { + uvc_uninit_video(video); + return -ENOMEM; + } + + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &urb->transfer_dma); + if (video->urb_buffer[i] == NULL) { + usb_free_urb(urb); + uvc_uninit_video(video); + return -ENOMEM; + } + + urb->dev = video->dev->udev; + urb->context = video; + urb->pipe = usb_rcvisocpipe(video->dev->udev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = ep->desc.bInterval; + urb->transfer_buffer = video->urb_buffer[i]; + urb->complete = uvc_video_complete; + urb->number_of_packets = npackets; + urb->transfer_buffer_length = size; + + for (j = 0; j < npackets; ++j) { + urb->iso_frame_desc[j].offset = j * psize; + urb->iso_frame_desc[j].length = psize; + } + + video->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize bulk URBs and allocate transfer buffers. The packet size is + * given by the endpoint. + */ +static int uvc_init_video_bulk(struct uvc_video_device *video, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + unsigned int pipe, i; + __u16 psize; + __u32 size; + + /* Compute the bulk URB size. Some devices set the maximum payload + * size to a value too high for memory-constrained devices. We must + * then transfer the payload accross multiple URBs. To be consistant + * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk + * URB. + */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; + size = video->streaming->ctrl.dwMaxPayloadTransferSize; + video->bulk.max_payload_size = size; + if (size > psize * UVC_MAX_ISO_PACKETS) + size = psize * UVC_MAX_ISO_PACKETS; + + pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress); + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { + uvc_uninit_video(video); + return -ENOMEM; + } + + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &urb->transfer_dma); + if (video->urb_buffer[i] == NULL) { + usb_free_urb(urb); + uvc_uninit_video(video); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, video->dev->udev, pipe, + video->urb_buffer[i], size, uvc_video_complete, + video); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + + video->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize isochronous/bulk URBs and allocate transfer buffers. + */ +static int uvc_init_video(struct uvc_video_device *video) +{ + struct usb_interface *intf = video->streaming->intf; + struct usb_host_interface *alts; + struct usb_host_endpoint *ep = NULL; + int intfnum = video->streaming->intfnum; + unsigned int bandwidth, psize, i; + int ret; + + video->last_fid = -1; + video->bulk.header_size = 0; + video->bulk.skip_payload = 0; + video->bulk.payload_size = 0; + + if (intf->num_altsetting > 1) { + /* Isochronous endpoint, select the alternate setting. */ + bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; + + if (bandwidth == 0) { + uvc_printk(KERN_WARNING, "device %s requested null " + "bandwidth, defaulting to lowest.\n", + video->vdev->name); + bandwidth = 1; + } + + for (i = 0; i < intf->num_altsetting; ++i) { + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + video->streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + /* Check if the bandwidth is high enough. */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize >= bandwidth) + break; + } + + if (i >= intf->num_altsetting) + return -EIO; + + if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) + return ret; + + ret = uvc_init_video_isoc(video, ep); + } else { + /* Bulk endpoint, proceed to URB initialization. */ + ep = uvc_find_endpoint(&intf->altsetting[0], + video->streaming->header.bEndpointAddress); + if (ep == NULL) + return -EIO; + + ret = uvc_init_video_bulk(video, ep); + } + + if (ret < 0) + return ret; + + /* Submit the URBs. */ + for (i = 0; i < UVC_URBS; ++i) { + if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) { + uvc_printk(KERN_ERR, "Failed to submit URB %u " + "(%d).\n", i, ret); + uvc_uninit_video(video); + return ret; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Stop streaming without disabling the video queue. + * + * To let userspace applications resume without trouble, we must not touch the + * video buffers in any way. We mark the device as frozen to make sure the URB + * completion handler won't try to cancel the queue when we kill the URBs. + */ +int uvc_video_suspend(struct uvc_video_device *video) +{ + if (!uvc_queue_streaming(&video->queue)) + return 0; + + video->frozen = 1; + uvc_uninit_video(video); + usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + return 0; +} + +/* + * Reconfigure the video interface and restart streaming if it was enable + * before suspend. + * + * If an error occurs, disable the video queue. This will wake all pending + * buffers, making sure userspace applications are notified of the problem + * instead of waiting forever. + */ +int uvc_video_resume(struct uvc_video_device *video) +{ + int ret; + + video->frozen = 0; + + if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) { + uvc_queue_enable(&video->queue, 0); + return ret; + } + + if (!uvc_queue_streaming(&video->queue)) + return 0; + + if ((ret = uvc_init_video(video)) < 0) + uvc_queue_enable(&video->queue, 0); + + return ret; +} + +/* ------------------------------------------------------------------------ + * Video device + */ + +/* + * Initialize the UVC video device by retrieving the default format and + * committing it. + * + * Some cameras (namely the Fuji Finepix) set the format and frame + * indexes to zero. The UVC standard doesn't clearly make this a spec + * violation, so try to silently fix the values if possible. + * + * This function is called before registering the device with V4L. + */ +int uvc_video_init(struct uvc_video_device *video) +{ + struct uvc_streaming_control *probe = &video->streaming->ctrl; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + unsigned int i; + int ret; + + if (video->streaming->nformats == 0) { + uvc_printk(KERN_INFO, "No supported video formats found.\n"); + return -EINVAL; + } + + /* Alternate setting 0 should be the default, yet the XBox Live Vision + * Cam (and possibly other devices) crash or otherwise misbehave if + * they don't receive a SET_INTERFACE request before any other video + * control request. + */ + usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + + /* Some webcams don't suport GET_DEF request on the probe control. We + * fall back to GET_CUR if GET_DEF fails. + */ + if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 && + (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + return ret; + + /* Check if the default format descriptor exists. Use the first + * available format otherwise. + */ + for (i = video->streaming->nformats; i > 0; --i) { + format = &video->streaming->format[i-1]; + if (format->index == probe->bFormatIndex) + break; + } + + if (format->nframes == 0) { + uvc_printk(KERN_INFO, "No frame descriptor found for the " + "default format.\n"); + return -EINVAL; + } + + /* Zero bFrameIndex might be correct. Stream-based formats (including + * MPEG-2 TS and DV) do not support frames but have a dummy frame + * descriptor with bFrameIndex set to zero. If the default frame + * descriptor is not found, use the first avalable frame. + */ + for (i = format->nframes; i > 0; --i) { + frame = &format->frame[i-1]; + if (frame->bFrameIndex == probe->bFrameIndex) + break; + } + + /* Commit the default settings. */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) + return ret; + + video->streaming->cur_format = format; + video->streaming->cur_frame = frame; + atomic_set(&video->active, 0); + + /* Select the video decoding function */ + if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + video->decode = uvc_video_decode_isight; + else if (video->streaming->intf->num_altsetting > 1) + video->decode = uvc_video_decode_isoc; + else + video->decode = uvc_video_decode_bulk; + + return 0; +} + +/* + * Enable or disable the video stream. + */ +int uvc_video_enable(struct uvc_video_device *video, int enable) +{ + int ret; + + if (!enable) { + uvc_uninit_video(video); + usb_set_interface(video->dev->udev, + video->streaming->intfnum, 0); + uvc_queue_enable(&video->queue, 0); + return 0; + } + + if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + return ret; + + return uvc_init_video(video); +} diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h new file mode 100644 index 000000000000..a995a780db1c --- /dev/null +++ b/drivers/media/video/uvc/uvcvideo.h @@ -0,0 +1,796 @@ +#ifndef _USB_VIDEO_H_ +#define _USB_VIDEO_H_ + +#include <linux/kernel.h> +#include <linux/videodev2.h> + + +/* + * Dynamic controls + */ + +/* Data types for UVC control data */ +#define UVC_CTRL_DATA_TYPE_RAW 0 +#define UVC_CTRL_DATA_TYPE_SIGNED 1 +#define UVC_CTRL_DATA_TYPE_UNSIGNED 2 +#define UVC_CTRL_DATA_TYPE_BOOLEAN 3 +#define UVC_CTRL_DATA_TYPE_ENUM 4 +#define UVC_CTRL_DATA_TYPE_BITMASK 5 + +/* Control flags */ +#define UVC_CONTROL_SET_CUR (1 << 0) +#define UVC_CONTROL_GET_CUR (1 << 1) +#define UVC_CONTROL_GET_MIN (1 << 2) +#define UVC_CONTROL_GET_MAX (1 << 3) +#define UVC_CONTROL_GET_RES (1 << 4) +#define UVC_CONTROL_GET_DEF (1 << 5) +/* Control should be saved at suspend and restored at resume. */ +#define UVC_CONTROL_RESTORE (1 << 6) +/* Control can be updated by the camera. */ +#define UVC_CONTROL_AUTO_UPDATE (1 << 7) + +#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ + UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ + UVC_CONTROL_GET_DEF) + +struct uvc_xu_control_info { + __u8 entity[16]; + __u8 index; + __u8 selector; + __u16 size; + __u32 flags; +}; + +struct uvc_xu_control_mapping { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; +}; + +struct uvc_xu_control { + __u8 unit; + __u8 selector; + __u16 size; + __u8 __user *data; +}; + +#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) +#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) +#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) + +#ifdef __KERNEL__ + +#include <linux/poll.h> + +/* -------------------------------------------------------------------------- + * UVC constants + */ + +#define SC_UNDEFINED 0x00 +#define SC_VIDEOCONTROL 0x01 +#define SC_VIDEOSTREAMING 0x02 +#define SC_VIDEO_INTERFACE_COLLECTION 0x03 + +#define PC_PROTOCOL_UNDEFINED 0x00 + +#define CS_UNDEFINED 0x20 +#define CS_DEVICE 0x21 +#define CS_CONFIGURATION 0x22 +#define CS_STRING 0x23 +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/* VideoControl class specific interface descriptor */ +#define VC_DESCRIPTOR_UNDEFINED 0x00 +#define VC_HEADER 0x01 +#define VC_INPUT_TERMINAL 0x02 +#define VC_OUTPUT_TERMINAL 0x03 +#define VC_SELECTOR_UNIT 0x04 +#define VC_PROCESSING_UNIT 0x05 +#define VC_EXTENSION_UNIT 0x06 + +/* VideoStreaming class specific interface descriptor */ +#define VS_UNDEFINED 0x00 +#define VS_INPUT_HEADER 0x01 +#define VS_OUTPUT_HEADER 0x02 +#define VS_STILL_IMAGE_FRAME 0x03 +#define VS_FORMAT_UNCOMPRESSED 0x04 +#define VS_FRAME_UNCOMPRESSED 0x05 +#define VS_FORMAT_MJPEG 0x06 +#define VS_FRAME_MJPEG 0x07 +#define VS_FORMAT_MPEG2TS 0x0a +#define VS_FORMAT_DV 0x0c +#define VS_COLORFORMAT 0x0d +#define VS_FORMAT_FRAME_BASED 0x10 +#define VS_FRAME_FRAME_BASED 0x11 +#define VS_FORMAT_STREAM_BASED 0x12 + +/* Endpoint type */ +#define EP_UNDEFINED 0x00 +#define EP_GENERAL 0x01 +#define EP_ENDPOINT 0x02 +#define EP_INTERRUPT 0x03 + +/* Request codes */ +#define RC_UNDEFINED 0x00 +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define GET_MIN 0x82 +#define GET_MAX 0x83 +#define GET_RES 0x84 +#define GET_LEN 0x85 +#define GET_INFO 0x86 +#define GET_DEF 0x87 + +/* VideoControl interface controls */ +#define VC_CONTROL_UNDEFINED 0x00 +#define VC_VIDEO_POWER_MODE_CONTROL 0x01 +#define VC_REQUEST_ERROR_CODE_CONTROL 0x02 + +/* Terminal controls */ +#define TE_CONTROL_UNDEFINED 0x00 + +/* Selector Unit controls */ +#define SU_CONTROL_UNDEFINED 0x00 +#define SU_INPUT_SELECT_CONTROL 0x01 + +/* Camera Terminal controls */ +#define CT_CONTROL_UNDEFINED 0x00 +#define CT_SCANNING_MODE_CONTROL 0x01 +#define CT_AE_MODE_CONTROL 0x02 +#define CT_AE_PRIORITY_CONTROL 0x03 +#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define CT_FOCUS_RELATIVE_CONTROL 0x07 +#define CT_FOCUS_AUTO_CONTROL 0x08 +#define CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define CT_IRIS_RELATIVE_CONTROL 0x0a +#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b +#define CT_ZOOM_RELATIVE_CONTROL 0x0c +#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d +#define CT_PANTILT_RELATIVE_CONTROL 0x0e +#define CT_ROLL_ABSOLUTE_CONTROL 0x0f +#define CT_ROLL_RELATIVE_CONTROL 0x10 +#define CT_PRIVACY_CONTROL 0x11 + +/* Processing Unit controls */ +#define PU_CONTROL_UNDEFINED 0x00 +#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define PU_BRIGHTNESS_CONTROL 0x02 +#define PU_CONTRAST_CONTROL 0x03 +#define PU_GAIN_CONTROL 0x04 +#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define PU_HUE_CONTROL 0x06 +#define PU_SATURATION_CONTROL 0x07 +#define PU_SHARPNESS_CONTROL 0x08 +#define PU_GAMMA_CONTROL 0x09 +#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a +#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b +#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c +#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d +#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e +#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f +#define PU_HUE_AUTO_CONTROL 0x10 +#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + +#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01 +#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02 +#define LXU_MOTOR_FOCUS_MOTOR_CONTROL 0x03 + +/* VideoStreaming interface controls */ +#define VS_CONTROL_UNDEFINED 0x00 +#define VS_PROBE_CONTROL 0x01 +#define VS_COMMIT_CONTROL 0x02 +#define VS_STILL_PROBE_CONTROL 0x03 +#define VS_STILL_COMMIT_CONTROL 0x04 +#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define VS_SYNC_DELAY_CONTROL 0x09 + +#define TT_VENDOR_SPECIFIC 0x0100 +#define TT_STREAMING 0x0101 + +/* Input Terminal types */ +#define ITT_VENDOR_SPECIFIC 0x0200 +#define ITT_CAMERA 0x0201 +#define ITT_MEDIA_TRANSPORT_INPUT 0x0202 + +/* Output Terminal types */ +#define OTT_VENDOR_SPECIFIC 0x0300 +#define OTT_DISPLAY 0x0301 +#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 + +/* External Terminal types */ +#define EXTERNAL_VENDOR_SPECIFIC 0x0400 +#define COMPOSITE_CONNECTOR 0x0401 +#define SVIDEO_CONNECTOR 0x0402 +#define COMPONENT_CONNECTOR 0x0403 + +#define UVC_TERM_INPUT 0x0000 +#define UVC_TERM_OUTPUT 0x8000 + +#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) +#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) +#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) +#define UVC_ENTITY_IS_ITERM(entity) \ + (((entity)->type & 0x8000) == UVC_TERM_INPUT) +#define UVC_ENTITY_IS_OTERM(entity) \ + (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + +#define UVC_STATUS_TYPE_CONTROL 1 +#define UVC_STATUS_TYPE_STREAMING 2 + +/* ------------------------------------------------------------------------ + * GUIDs + */ +#define UVC_GUID_UVC_CAMERA \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} +#define UVC_GUID_UVC_OUTPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} +#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03} +#define UVC_GUID_UVC_PROCESSING \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01} +#define UVC_GUID_UVC_SELECTOR \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} + +#define UVC_GUID_LOGITECH_DEV_INFO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} +#define UVC_GUID_LOGITECH_USER_HW \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f} +#define UVC_GUID_LOGITECH_VIDEO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50} +#define UVC_GUID_LOGITECH_MOTOR \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56} + +#define UVC_GUID_FORMAT_MJPEG \ + { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2 \ + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_NV12 \ + { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YV12 \ + { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_I420 \ + { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_UYVY \ + { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y800 \ + { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_BY8 \ + { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + + +/* ------------------------------------------------------------------------ + * Driver specific constants. + */ + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) + +/* Number of isochronous URBs. */ +#define UVC_URBS 5 +/* Maximum number of packets per isochronous URB. */ +#define UVC_MAX_ISO_PACKETS 40 +/* Maximum frame size in bytes, for sanity checking. */ +#define UVC_MAX_FRAME_SIZE (16*1024*1024) +/* Maximum number of video buffers. */ +#define UVC_MAX_VIDEO_BUFFERS 32 + +#define UVC_CTRL_CONTROL_TIMEOUT 300 +#define UVC_CTRL_STREAMING_TIMEOUT 1000 + +/* Devices quirks */ +#define UVC_QUIRK_STATUS_INTERVAL 0x00000001 +#define UVC_QUIRK_PROBE_MINMAX 0x00000002 +#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004 +#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 +#define UVC_QUIRK_STREAM_NO_FID 0x00000010 +#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 + +/* Format flags */ +#define UVC_FMT_FLAG_COMPRESSED 0x00000001 +#define UVC_FMT_FLAG_STREAM 0x00000002 + +/* ------------------------------------------------------------------------ + * Structures. + */ + +struct uvc_device; + +/* TODO: Put the most frequently accessed fields at the beginning of + * structures to maximize cache efficiency. + */ +struct uvc_streaming_control { + __u16 bmHint; + __u8 bFormatIndex; + __u8 bFrameIndex; + __u32 dwFrameInterval; + __u16 wKeyFrameRate; + __u16 wPFrameRate; + __u16 wCompQuality; + __u16 wCompWindowSize; + __u16 wDelay; + __u32 dwMaxVideoFrameSize; + __u32 dwMaxPayloadTransferSize; + __u32 dwClockFrequency; + __u8 bmFramingInfo; + __u8 bPreferedVersion; + __u8 bMinVersion; + __u8 bMaxVersion; +}; + +struct uvc_menu_info { + __u32 value; + __u8 name[32]; +}; + +struct uvc_control_info { + struct list_head list; + struct list_head mappings; + + __u8 entity[16]; + __u8 index; + __u8 selector; + + __u16 size; + __u32 flags; +}; + +struct uvc_control_mapping { + struct list_head list; + + struct uvc_control_info *ctrl; + + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; + + struct uvc_menu_info *menu_info; + __u32 menu_count; +}; + +struct uvc_control { + struct uvc_entity *entity; + struct uvc_control_info *info; + + __u8 index; /* Used to match the uvc_control entry with a + uvc_control_info. */ + __u8 dirty : 1, + loaded : 1, + modified : 1; + + __u8 *data; +}; + +struct uvc_format_desc { + char *name; + __u8 guid[16]; + __u32 fcc; +}; + +/* The term 'entity' refers to both UVC units and UVC terminals. + * + * The type field is either the terminal type (wTerminalType in the terminal + * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). + * As the bDescriptorSubtype field is one byte long, the type value will + * always have a null MSB for units. All terminal types defined by the UVC + * specification have a non-null MSB, so it is safe to use the MSB to + * differentiate between units and terminals as long as the descriptor parsing + * code makes sure terminal types have a non-null MSB. + * + * For terminals, the type's most significant bit stores the terminal + * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should + * always be accessed with the UVC_ENTITY_* macros and never directly. + */ + +struct uvc_entity { + struct list_head list; /* Entity as part of a UVC device. */ + struct list_head chain; /* Entity as part of a video device + * chain. */ + __u8 id; + __u16 type; + char name[64]; + + union { + struct { + __u16 wObjectiveFocalLengthMin; + __u16 wObjectiveFocalLengthMax; + __u16 wOcularFocalLength; + __u8 bControlSize; + __u8 *bmControls; + } camera; + + struct { + __u8 bControlSize; + __u8 *bmControls; + __u8 bTransportModeSize; + __u8 *bmTransportModes; + } media; + + struct { + __u8 bSourceID; + } output; + + struct { + __u8 bSourceID; + __u16 wMaxMultiplier; + __u8 bControlSize; + __u8 *bmControls; + __u8 bmVideoStandards; + } processing; + + struct { + __u8 bNrInPins; + __u8 *baSourceID; + } selector; + + struct { + __u8 guidExtensionCode[16]; + __u8 bNumControls; + __u8 bNrInPins; + __u8 *baSourceID; + __u8 bControlSize; + __u8 *bmControls; + __u8 *bmControlsType; + } extension; + }; + + unsigned int ncontrols; + struct uvc_control *controls; +}; + +struct uvc_frame { + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u8 bFrameIntervalType; + __u32 dwDefaultFrameInterval; + __u32 *dwFrameInterval; +}; + +struct uvc_format { + __u8 type; + __u8 index; + __u8 bpp; + __u8 colorspace; + __u32 fcc; + __u32 flags; + + char name[32]; + + unsigned int nframes; + struct uvc_frame *frame; +}; + +struct uvc_streaming_header { + __u8 bNumFormats; + __u8 bEndpointAddress; + __u8 bTerminalLink; + __u8 bControlSize; + __u8 *bmaControls; + /* The following fields are used by input headers only. */ + __u8 bmInfo; + __u8 bStillCaptureMethod; + __u8 bTriggerSupport; + __u8 bTriggerUsage; +}; + +struct uvc_streaming { + struct list_head list; + + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; + + struct uvc_streaming_header header; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; +}; + +enum uvc_buffer_state { + UVC_BUF_STATE_IDLE = 0, + UVC_BUF_STATE_QUEUED = 1, + UVC_BUF_STATE_ACTIVE = 2, + UVC_BUF_STATE_DONE = 3, + UVC_BUF_STATE_ERROR = 4, +}; + +struct uvc_buffer { + unsigned long vma_use_count; + struct list_head stream; + + /* Touched by interrupt handler. */ + struct v4l2_buffer buf; + struct list_head queue; + wait_queue_head_t wait; + enum uvc_buffer_state state; +}; + +#define UVC_QUEUE_STREAMING (1 << 0) +#define UVC_QUEUE_DISCONNECTED (1 << 1) +#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) + +struct uvc_video_queue { + void *mem; + unsigned int flags; + __u32 sequence; + + unsigned int count; + unsigned int buf_size; + struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; + struct mutex mutex; /* protects buffers and mainqueue */ + spinlock_t irqlock; /* protects irqqueue */ + + struct list_head mainqueue; + struct list_head irqqueue; +}; + +struct uvc_video_device { + struct uvc_device *dev; + struct video_device *vdev; + atomic_t active; + unsigned int frozen : 1; + + struct list_head iterms; + struct uvc_entity *oterm; + struct uvc_entity *processing; + struct uvc_entity *selector; + struct list_head extensions; + struct mutex ctrl_mutex; + + struct uvc_video_queue queue; + + /* Video streaming object, must always be non-NULL. */ + struct uvc_streaming *streaming; + + void (*decode) (struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf); + + /* Context data used by the bulk completion handler. */ + struct { + __u8 header[256]; + unsigned int header_size; + int skip_payload; + __u32 payload_size; + __u32 max_payload_size; + } bulk; + + struct urb *urb[UVC_URBS]; + char *urb_buffer[UVC_URBS]; + + __u8 last_fid; +}; + +enum uvc_device_state { + UVC_DEV_DISCONNECTED = 1, +}; + +struct uvc_device { + struct usb_device *udev; + struct usb_interface *intf; + __u32 quirks; + int intfnum; + char name[32]; + + enum uvc_device_state state; + struct kref kref; + struct list_head list; + + /* Video control interface */ + __u16 uvc_version; + __u32 clock_frequency; + + struct list_head entities; + + struct uvc_video_device video; + + /* Status Interrupt Endpoint */ + struct usb_host_endpoint *int_ep; + struct urb *int_urb; + __u8 status[16]; + struct input_dev *input; + + /* Video Streaming interfaces */ + struct list_head streaming; +}; + +enum uvc_handle_state { + UVC_HANDLE_PASSIVE = 0, + UVC_HANDLE_ACTIVE = 1, +}; + +struct uvc_fh { + struct uvc_video_device *device; + enum uvc_handle_state state; +}; + +struct uvc_driver { + struct usb_driver driver; + + struct mutex open_mutex; /* protects from open/disconnect race */ + + struct list_head devices; /* struct uvc_device list */ + struct list_head controls; /* struct uvc_control_info list */ + struct mutex ctrl_mutex; /* protects controls and devices + lists */ +}; + +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +#define UVC_TRACE_PROBE (1 << 0) +#define UVC_TRACE_DESCR (1 << 1) +#define UVC_TRACE_CONTROL (1 << 2) +#define UVC_TRACE_FORMAT (1 << 3) +#define UVC_TRACE_CAPTURE (1 << 4) +#define UVC_TRACE_CALLS (1 << 5) +#define UVC_TRACE_IOCTL (1 << 6) +#define UVC_TRACE_FRAME (1 << 7) +#define UVC_TRACE_SUSPEND (1 << 8) +#define UVC_TRACE_STATUS (1 << 9) + +extern unsigned int uvc_trace_param; + +#define uvc_trace(flag, msg...) \ + do { \ + if (uvc_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " msg); \ + } while (0) + +#define uvc_printk(level, msg...) \ + printk(level "uvcvideo: " msg) + +#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ + "%02x%02x%02x%02x%02x%02x" +#define UVC_GUID_ARGS(guid) \ + (guid)[3], (guid)[2], (guid)[1], (guid)[0], \ + (guid)[5], (guid)[4], \ + (guid)[7], (guid)[6], \ + (guid)[8], (guid)[9], \ + (guid)[10], (guid)[11], (guid)[12], \ + (guid)[13], (guid)[14], (guid)[15] + +/* -------------------------------------------------------------------------- + * Internal functions. + */ + +/* Core driver */ +extern struct uvc_driver uvc_driver; +extern void uvc_delete(struct kref *kref); + +/* Video buffers queue management. */ +extern void uvc_queue_init(struct uvc_video_queue *queue); +extern int uvc_alloc_buffers(struct uvc_video_queue *queue, + unsigned int nbuffers, unsigned int buflength); +extern int uvc_free_buffers(struct uvc_video_queue *queue); +extern int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking); +extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); +extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); +extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); +extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait); +static inline int uvc_queue_streaming(struct uvc_video_queue *queue) +{ + return queue->flags & UVC_QUEUE_STREAMING; +} + +/* V4L2 interface */ +extern struct file_operations uvc_fops; + +/* Video */ +extern int uvc_video_init(struct uvc_video_device *video); +extern int uvc_video_suspend(struct uvc_video_device *video); +extern int uvc_video_resume(struct uvc_video_device *video); +extern int uvc_video_enable(struct uvc_video_device *video, int enable); +extern int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe); +extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size); +extern int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe); + +/* Status */ +extern int uvc_status_init(struct uvc_device *dev); +extern void uvc_status_cleanup(struct uvc_device *dev); +extern int uvc_status_suspend(struct uvc_device *dev); +extern int uvc_status_resume(struct uvc_device *dev); + +/* Controls */ +extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping); +extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl); + +extern int uvc_ctrl_add_info(struct uvc_control_info *info); +extern int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping); +extern int uvc_ctrl_init_device(struct uvc_device *dev); +extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); +extern int uvc_ctrl_resume_device(struct uvc_device *dev); +extern void uvc_ctrl_init(void); + +extern int uvc_ctrl_begin(struct uvc_video_device *video); +extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 0); +} +static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 1); +} + +extern int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); +extern int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); + +extern int uvc_xu_ctrl_query(struct uvc_video_device *video, + struct uvc_xu_control *ctrl, int set); + +/* Utility functions */ +extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold); +extern uint32_t uvc_fraction_to_interval(uint32_t numerator, + uint32_t denominator); +extern struct usb_host_endpoint *uvc_find_endpoint( + struct usb_host_interface *alts, __u8 epaddr); + +/* Quirks support */ +void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf); + +#endif /* __KERNEL__ */ + +#endif diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 982f4463896c..0a88c44ace00 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -331,7 +331,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) { @@ -1129,6 +1129,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/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 31e8af0ba278..67a661cf5219 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -51,12 +51,51 @@ #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" +struct std_descr { + v4l2_std_id std; + const char *descr; +}; + +static const struct std_descr standards[] = { + { V4L2_STD_NTSC, "NTSC" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" }, + { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" }, + { V4L2_STD_NTSC_443, "NTSC-443" }, + { V4L2_STD_PAL, "PAL" }, + { V4L2_STD_PAL_BG, "PAL-BG" }, + { V4L2_STD_PAL_B, "PAL-B" }, + { V4L2_STD_PAL_B1, "PAL-B1" }, + { V4L2_STD_PAL_G, "PAL-G" }, + { V4L2_STD_PAL_H, "PAL-H" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_D, "PAL-D" }, + { V4L2_STD_PAL_D1, "PAL-D1" }, + { V4L2_STD_PAL_K, "PAL-K" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_PAL_60, "PAL-60" }, + { V4L2_STD_SECAM, "SECAM" }, + { V4L2_STD_SECAM_B, "SECAM-B" }, + { V4L2_STD_SECAM_G, "SECAM-G" }, + { V4L2_STD_SECAM_H, "SECAM-H" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_D, "SECAM-D" }, + { V4L2_STD_SECAM_K, "SECAM-K" }, + { V4L2_STD_SECAM_K1, "SECAM-K1" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-Lc" }, + { 0, "Unknown" } +}; + /* video4linux standard ID conversion to standard name */ -char *v4l2_norm_to_name(v4l2_std_id id) +const char *v4l2_norm_to_name(v4l2_std_id id) { - char *name; u32 myid = id; + int i; /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle 64 bit comparations. So, on that architecture, with some gcc @@ -64,110 +103,17 @@ char *v4l2_norm_to_name(v4l2_std_id id) */ BUG_ON(myid != id); - switch (myid) { - case V4L2_STD_PAL: - name = "PAL"; - break; - case V4L2_STD_PAL_BG: - name = "PAL-BG"; - break; - case V4L2_STD_PAL_DK: - name = "PAL-DK"; - break; - case V4L2_STD_PAL_B: - name = "PAL-B"; - break; - case V4L2_STD_PAL_B1: - name = "PAL-B1"; - break; - case V4L2_STD_PAL_G: - name = "PAL-G"; - break; - case V4L2_STD_PAL_H: - name = "PAL-H"; - break; - case V4L2_STD_PAL_I: - name = "PAL-I"; - break; - case V4L2_STD_PAL_D: - name = "PAL-D"; - break; - case V4L2_STD_PAL_D1: - name = "PAL-D1"; - break; - case V4L2_STD_PAL_K: - name = "PAL-K"; - break; - case V4L2_STD_PAL_M: - name = "PAL-M"; - break; - case V4L2_STD_PAL_N: - name = "PAL-N"; - break; - case V4L2_STD_PAL_Nc: - name = "PAL-Nc"; - break; - case V4L2_STD_PAL_60: - name = "PAL-60"; - break; - case V4L2_STD_NTSC: - name = "NTSC"; - break; - case V4L2_STD_NTSC_M: - name = "NTSC-M"; - break; - case V4L2_STD_NTSC_M_JP: - name = "NTSC-M-JP"; - break; - case V4L2_STD_NTSC_443: - name = "NTSC-443"; - break; - case V4L2_STD_NTSC_M_KR: - name = "NTSC-M-KR"; - break; - case V4L2_STD_SECAM: - name = "SECAM"; - break; - case V4L2_STD_SECAM_DK: - name = "SECAM-DK"; - break; - case V4L2_STD_SECAM_B: - name = "SECAM-B"; - break; - case V4L2_STD_SECAM_D: - name = "SECAM-D"; - break; - case V4L2_STD_SECAM_G: - name = "SECAM-G"; - break; - case V4L2_STD_SECAM_H: - name = "SECAM-H"; - break; - case V4L2_STD_SECAM_K: - name = "SECAM-K"; - break; - case V4L2_STD_SECAM_K1: - name = "SECAM-K1"; - break; - case V4L2_STD_SECAM_L: - name = "SECAM-L"; - break; - case V4L2_STD_SECAM_LC: - name = "SECAM-LC"; - break; - default: - name = "Unknown"; - break; - } - - return name; + for (i = 0; standards[i].std; i++) + if (myid == standards[i].std) + break; + return standards[i].descr; } EXPORT_SYMBOL(v4l2_norm_to_name); /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, char *name) + int id, const char *name) { u32 index = vs->index; @@ -1218,95 +1164,40 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms,curr_id=0; - unsigned int index = p->index,i; - - if (index<0) { - ret=-EINVAL; - break; - } - - /* Return norm array on a canonical way */ - for (i=0;i<= index && id; i++) { - if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { - curr_id = V4L2_STD_PAL; - } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { - curr_id = V4L2_STD_PAL_BG; - } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { - curr_id = V4L2_STD_PAL_DK; - } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { - curr_id = V4L2_STD_PAL_B; - } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { - curr_id = V4L2_STD_PAL_B1; - } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { - curr_id = V4L2_STD_PAL_G; - } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { - curr_id = V4L2_STD_PAL_H; - } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { - curr_id = V4L2_STD_PAL_I; - } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { - curr_id = V4L2_STD_PAL_D; - } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { - curr_id = V4L2_STD_PAL_D1; - } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { - curr_id = V4L2_STD_PAL_K; - } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { - curr_id = V4L2_STD_PAL_M; - } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { - curr_id = V4L2_STD_PAL_N; - } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { - curr_id = V4L2_STD_PAL_Nc; - } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { - curr_id = V4L2_STD_PAL_60; - } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { - curr_id = V4L2_STD_NTSC; - } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { - curr_id = V4L2_STD_NTSC_M; - } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { - curr_id = V4L2_STD_NTSC_M_JP; - } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { - curr_id = V4L2_STD_NTSC_443; - } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { - curr_id = V4L2_STD_NTSC_M_KR; - } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { - curr_id = V4L2_STD_SECAM; - } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { - curr_id = V4L2_STD_SECAM_DK; - } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { - curr_id = V4L2_STD_SECAM_B; - } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { - curr_id = V4L2_STD_SECAM_D; - } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { - curr_id = V4L2_STD_SECAM_G; - } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { - curr_id = V4L2_STD_SECAM_H; - } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { - curr_id = V4L2_STD_SECAM_K; - } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { - curr_id = V4L2_STD_SECAM_K1; - } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { - curr_id = V4L2_STD_SECAM_L; - } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { - curr_id = V4L2_STD_SECAM_LC; - } else { + v4l2_std_id id = vfd->tvnorms, curr_id = 0; + unsigned int index = p->index, i, j = 0; + const char *descr = ""; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) + j++; + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) break; - } - id &= ~curr_id; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; } - if (i<=index) + if (i <= index) return -EINVAL; - v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id)); + v4l2_video_std_construct(p, curr_id, descr); p->index = index; - dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " + dbgarg(cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " "framelines=%d\n", p->index, (unsigned long long)p->id, p->name, p->frameperiod.numerator, p->frameperiod.denominator, p->framelines); - ret=0; + ret = 0; break; } case VIDIOC_G_STD: diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 845be1864f68..5ff9a58b6135 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -327,13 +327,14 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) int hmax = buf->vb.height; int wmax = buf->vb.width; struct timeval ts; - char *tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); + char *tmpbuf; void *vbuf = videobuf_to_vmalloc(&buf->vb); - if (!tmpbuf) + if (!vbuf) return; - if (!vbuf) + tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); + if (!tmpbuf) return; for (h = 0; h < hmax; h++) { diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index fac0deba24af..a1f76ee032e7 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -130,8 +130,6 @@ static int vp27smpx_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - snprintf(client->name, sizeof(client->name) - 1, "vp27smpx"); - v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); @@ -154,11 +152,18 @@ static int vp27smpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct i2c_device_id vp27smpx_id[] = { + { "vp27smpx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vp27smpx_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "vp27smpx", .driverid = I2C_DRIVERID_VP27SMPX, .command = vp27smpx_command, .probe = vp27smpx_probe, .remove = vp27smpx_remove, + .id_table = vp27smpx_id, }; diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index 0f8ed8461fba..fc50299caa36 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -313,11 +313,18 @@ static int wm8739_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id wm8739_id[] = { + { "wm8739", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8739_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "wm8739", .driverid = I2C_DRIVERID_WM8739, .command = wm8739_command, .probe = wm8739_probe, .remove = wm8739_remove, + .id_table = wm8739_id, }; diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 67a409e60c46..506378a508b9 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -216,11 +216,18 @@ static int wm8775_remove(struct i2c_client *client) return 0; } +static const struct i2c_device_id wm8775_id[] = { + { "wm8775", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8775_id); + static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "wm8775", .driverid = I2C_DRIVERID_WM8775, .command = wm8775_command, .probe = wm8775_probe, .remove = wm8775_remove, + .id_table = wm8775_id, }; diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 363dd2b9475c..e5c4e9f5193f 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -38,7 +38,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/page-flags.h> -#include <linux/byteorder/generic.h> +#include <asm/byteorder.h> #include <asm/page.h> #include <asm/uaccess.h> diff --git a/drivers/media/video/zoran.h b/drivers/media/video/zoran.h index 81cc3b00a079..46b7ad477ceb 100644 --- a/drivers/media/video/zoran.h +++ b/drivers/media/video/zoran.h @@ -285,7 +285,7 @@ struct zoran_mapping { struct zoran_jpg_buffer { struct zoran_mapping *map; - u32 *frag_tab; /* addresses of frag table */ + __le32 *frag_tab; /* addresses of frag table */ u32 frag_tab_bus; /* same value cached to save time in ISR */ enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ struct zoran_sync bs; /* DONE: info to return to application */ @@ -450,7 +450,7 @@ struct zoran { unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ /* zr36057's code buffer table */ - u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ int jpg_pend[BUZ_MAX_FRAME]; diff --git a/drivers/media/video/zoran_device.c b/drivers/media/video/zoran_device.c index 7b60533efe45..88d369708e4c 100644 --- a/drivers/media/video/zoran_device.c +++ b/drivers/media/video/zoran_device.c @@ -31,7 +31,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/vmalloc.h> -#include <linux/byteorder/generic.h> #include <linux/interrupt.h> #include <linux/proc_fs.h> @@ -47,6 +46,7 @@ #include <linux/delay.h> #include <linux/wait.h> +#include <asm/byteorder.h> #include <asm/io.h> #include "videocodec.h" @@ -1320,7 +1320,7 @@ error_handler (struct zoran *zr, if (i) { /* Rotate stat_comm entries to make current entry first */ int j; - u32 bus_addr[BUZ_NUM_STAT_COM]; + __le32 bus_addr[BUZ_NUM_STAT_COM]; /* Here we are copying the stat_com array, which * is already in little endian format, so diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index 0134bec1e399..5394d7a5cfee 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -52,7 +52,6 @@ #include <linux/pci.h> #include <linux/vmalloc.h> #include <linux/wait.h> -#include <linux/byteorder/generic.h> #include <linux/interrupt.h> #include <linux/i2c.h> @@ -74,6 +73,7 @@ #include <media/v4l2-common.h> #include "videocodec.h" +#include <asm/byteorder.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/proc_fs.h> @@ -495,7 +495,7 @@ jpg_fbuffer_alloc (struct file *file) jpg_fbuffer_free(file); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab = (u32 *) mem; + fh->jpg_buffers.buffer[i].frag_tab = (__le32 *) mem; fh->jpg_buffers.buffer[i].frag_tab_bus = virt_to_bus((void *) mem); @@ -1167,7 +1167,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); @@ -3436,7 +3436,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); @@ -4375,7 +4375,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); @@ -4506,7 +4506,7 @@ zoran_mmap (struct file *file, if (todo > fraglen) todo = fraglen; pos = - le32_to_cpu((unsigned long) fh->jpg_buffers. + le32_to_cpu(fh->jpg_buffers. buffer[i].frag_tab[2 * j]); /* should just be pos on i386 */ page = virt_to_phys(bus_to_virt(pos)) |