summaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/Kconfig3
-rw-r--r--drivers/media/Makefile5
-rw-r--r--drivers/media/common/ir-keymaps.c38
-rw-r--r--drivers/media/common/tuners/Kconfig51
-rw-r--r--drivers/media/common/tuners/Makefile1
-rw-r--r--drivers/media/common/tuners/mxl5005s.c4110
-rw-r--r--drivers/media/common/tuners/mxl5005s.h131
-rw-r--r--drivers/media/common/tuners/tda18271-common.c38
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c219
-rw-r--r--drivers/media/common/tuners/tda18271-priv.h9
-rw-r--r--drivers/media/common/tuners/tda827x.c4
-rw-r--r--drivers/media/common/tuners/tea5761.c2
-rw-r--r--drivers/media/common/tuners/tea5767.c6
-rw-r--r--drivers/media/common/tuners/tuner-i2c.h8
-rw-r--r--drivers/media/common/tuners/tuner-simple.c6
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c87
-rw-r--r--drivers/media/common/tuners/xc5000.c39
-rw-r--r--drivers/media/common/tuners/xc5000.h22
-rw-r--r--drivers/media/common/tuners/xc5000_priv.h3
-rw-r--r--drivers/media/dvb/b2c2/flexcop-fe-tuner.c2
-rw-r--r--drivers/media/dvb/b2c2/flexcop-usb.c2
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig1
-rw-r--r--drivers/media/dvb/cinergyT2/Kconfig2
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c46
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.c28
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c12
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig3
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c21
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-firmware.c2
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c27
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c10
-rw-r--r--drivers/media/dvb/dvb-usb/m920x.c7
-rw-r--r--drivers/media/dvb/dvb-usb/umt-010.c2
-rw-r--r--drivers/media/dvb/frontends/Kconfig18
-rw-r--r--drivers/media/dvb/frontends/au8522.c29
-rw-r--r--drivers/media/dvb/frontends/dib0070.h15
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h15
-rw-r--r--drivers/media/dvb/frontends/itd1000.c2
-rw-r--r--drivers/media/dvb/frontends/mt312.c9
-rw-r--r--drivers/media/dvb/frontends/mt312.h4
-rw-r--r--drivers/media/dvb/frontends/or51132.c6
-rw-r--r--drivers/media/dvb/frontends/stv0299.c15
-rw-r--r--drivers/media/dvb/frontends/tda10023.c20
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c29
-rw-r--r--drivers/media/dvb/ttpci/Kconfig3
-rw-r--r--drivers/media/dvb/ttpci/av7110.c9
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c34
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.c5
-rw-r--r--drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c2
-rw-r--r--drivers/media/dvb/ttusb-dec/Kconfig3
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c25
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusbdecfe.c10
-rw-r--r--drivers/media/video/Kconfig20
-rw-r--r--drivers/media/video/Makefile4
-rw-r--r--drivers/media/video/au0828/Kconfig3
-rw-r--r--drivers/media/video/au0828/au0828-cards.c18
-rw-r--r--drivers/media/video/au0828/au0828-dvb.c12
-rw-r--r--drivers/media/video/bt8xx/Kconfig3
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c5
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-risc.c8
-rw-r--r--drivers/media/video/btcx-risc.c2
-rw-r--r--drivers/media/video/btcx-risc.h4
-rw-r--r--drivers/media/video/cs5345.c7
-rw-r--r--drivers/media/video/cs53l32a.c10
-rw-r--r--drivers/media/video/cx18/Kconfig7
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c154
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h16
-rw-r--r--drivers/media/video/cx18/cx18-cards.c105
-rw-r--r--drivers/media/video/cx18/cx18-cards.h55
-rw-r--r--drivers/media/video/cx18/cx18-controls.c6
-rw-r--r--drivers/media/video/cx18/cx18-driver.c57
-rw-r--r--drivers/media/video/cx18/cx18-driver.h12
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c57
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c19
-rw-r--r--drivers/media/video/cx18/cx18-fileops.h9
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c72
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h1
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c12
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c12
-rw-r--r--drivers/media/video/cx18/cx18-irq.c12
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c8
-rw-r--r--drivers/media/video/cx18/cx18-queue.c22
-rw-r--r--drivers/media/video/cx18/cx18-queue.h4
-rw-r--r--drivers/media/video/cx18/cx18-streams.c50
-rw-r--r--drivers/media/video/cx18/cx18-streams.h2
-rw-r--r--drivers/media/video/cx23885/Kconfig6
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c36
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c8
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c7
-rw-r--r--drivers/media/video/cx25840/Kconfig1
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c9
-rw-r--r--drivers/media/video/cx88/Kconfig6
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c6
-rw-r--r--drivers/media/video/cx88/cx88-cards.c13
-rw-r--r--drivers/media/video/cx88/cx88-core.c8
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c253
-rw-r--r--drivers/media/video/em28xx/Kconfig3
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c18
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c12
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c11
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c32
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2
-rw-r--r--drivers/media/video/ivtv/Kconfig4
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c8
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h10
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c13
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.h6
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c8
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.c14
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c43
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.h2
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.c3
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h4
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.h2
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c6
-rw-r--r--drivers/media/video/m52790.c9
-rw-r--r--drivers/media/video/msp3400-driver.c17
-rw-r--r--drivers/media/video/mt9m001.c5
-rw-r--r--drivers/media/video/mt9v022.c7
-rw-r--r--drivers/media/video/pvrusb2/Kconfig4
-rw-r--r--drivers/media/video/pxa_camera.c4
-rw-r--r--drivers/media/video/saa7115.c40
-rw-r--r--drivers/media/video/saa7127.c9
-rw-r--r--drivers/media/video/saa7134/Kconfig3
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c8
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c56
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c6
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c181
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c40
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c9
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2
-rw-r--r--drivers/media/video/saa717x.c9
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c2
-rw-r--r--drivers/media/video/soc_camera.c16
-rw-r--r--drivers/media/video/stk-webcam.c7
-rw-r--r--drivers/media/video/tcm825x.c7
-rw-r--r--drivers/media/video/tlv320aic23b.c6
-rw-r--r--drivers/media/video/tuner-core.c103
-rw-r--r--drivers/media/video/tvaudio.c13
-rw-r--r--drivers/media/video/tveeprom.c10
-rw-r--r--drivers/media/video/upd64031a.c6
-rw-r--r--drivers/media/video/upd64083.c6
-rw-r--r--drivers/media/video/usbvideo/quickcam_messenger.c2
-rw-r--r--drivers/media/video/usbvision/Kconfig2
-rw-r--r--drivers/media/video/uvc/Makefile3
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c1256
-rw-r--r--drivers/media/video/uvc/uvc_driver.c1955
-rw-r--r--drivers/media/video/uvc/uvc_isight.c134
-rw-r--r--drivers/media/video/uvc/uvc_queue.c477
-rw-r--r--drivers/media/video/uvc/uvc_status.c207
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c1105
-rw-r--r--drivers/media/video/uvc/uvc_video.c934
-rw-r--r--drivers/media/video/uvc/uvcvideo.h796
-rw-r--r--drivers/media/video/videobuf-core.c3
-rw-r--r--drivers/media/video/videodev.c245
-rw-r--r--drivers/media/video/vivi.c7
-rw-r--r--drivers/media/video/vp27smpx.c9
-rw-r--r--drivers/media/video/wm8739.c7
-rw-r--r--drivers/media/video/wm8775.c7
-rw-r--r--drivers/media/video/zc0301/zc0301_core.c2
-rw-r--r--drivers/media/video/zoran.h4
-rw-r--r--drivers/media/video/zoran_device.c4
-rw-r--r--drivers/media/video/zoran_driver.c12
169 files changed, 13021 insertions, 1308 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index ddf57e135c6..7a7803b5d49 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 73f742c7e81..09a829d8a7e 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 a3485817e46..8fa91f846d5 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 5be85ff53e1..85482960d01 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 236d9932fd9..55f7e670629 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 00000000000..0dc2bef9f6a
--- /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 00000000000..396db150bf0
--- /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 e27a7620a32..6fb5b458656 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 b262100ae89..93063c6fbbf 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 2bc5eb368ea..81a739365f8 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 d30d2c9094d..8555d9cf905 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 b93cdef9ac7..b23dadeecd0 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 f6e7d7ad842..1f5646334a8 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 3ad6c8e0b04..cb1c7141f0c 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 be8d903171b..266c255cf0d 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 9e9003cffc7..0cbde17bfbb 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 43d35bdb221..7cf4f5bdb2e 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 0ee80f9d19b..c910715addc 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 13b2d19341d..a72a9887fe7 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 7b0ea3bdfaf..f9d087669d5 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 449fb5c3d0b..ae0d76a5d51 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 d1239b8342f..7588db1319d 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 3d778c5aba6..c03513b2cca 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 f5010e8671b..a824f3719f8 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 8cbdb0ec67e..588fbe105c2 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 56d871cfd7f..c2334aef414 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 4c1cff9feb2..f00a0eb4042 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 346223856f5..c4d40fe01d5 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 e1112e39fb6..733a7ff7b20 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 0a8ac64a4e3..037f7ffb47b 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 9a942afaf0a..2653120673b 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 a12e6f784fd..54626a0dbf6 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 9e7653bb3b6..118aab1a3e5 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 6d238460592..c20553c4da1 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 084a280c2d7..03900d241a7 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 786e37d3388..3eedfdf505b 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 081bd81f3da..07c4d12ed5b 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 04c562ccf99..600dad6b41e 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 081ca3398c7..5ac9b15920f 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 de796eab391..29e3bb5496b 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 c7b5785f81f..5ed32544de3 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 17556183e87..35435bef8e7 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 0727b80bc4d..c6ff5b82ff8 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 49973846373..a0d63865356 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 ae882432dd3..07643e01009 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 747e7f1a626..f05d43d8b5c 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 3e6b650fbb8..ec55a968f20 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 9d81074b31d..3a3f5279e92 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 732ce4de512..5d2d81ab237 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 83611012ef3..a23cc0aa17d 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 42eee04daa5..fefdc05e84a 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 eb5eaeccd7c..443af24097f 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 fe743aa7f64..5ccb0aeca8c 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 a352c6e31f0..ecbbfaab24d 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 cab277fafa6..52b2491581a 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 a2a6983444f..898e12395e7 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 1371b4e4b5f..c6d47059038 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 7431ef6de9f..24a34fc1f2b 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 f20a01cfc73..8ef0424c26c 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 2ca3e9cfb2b..0165aac533b 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 e5979f77504..0af586876e7 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 ce0840ccd59..f42701f82e7 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 503e6c6d7b6..861bc811282 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 2a429f9e32c..03411503457 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 2dfd0afc62d..d965af860ab 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 acc4b47f1d1..9aefdc5ea79 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 66864904c99..faca43eb940 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 786901d72e9..c172823ce1d 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 f5e3ba1f535..c26e0ef5b07 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 bca249bdd33..dc2dd945d4c 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 2bdac5ebbb0..87cf4102166 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 8f5ed9b4bf8..2b810bb2a4c 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 2ee939193bb..de14ab59a20 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 65efe69d939..cae38985b13 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 69303065a29..1e537fe04a2 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 16cdafbd24c..46da0282fc7 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 19253e6b867..b302833f6f9 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 41bac8856b5..525c328f748 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 18c88d1e483..680bc4e35b7 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 dbdcb86ec5a..4151f1e5493 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 6e14f8bda55..25114a5cbd5 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 0c5f328bca5..2a5ccef9185 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 65af1bb507c..6990b77c620 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 f86c8a6fa6e..91423b9863a 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 afb141b2027..1b921a33609 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 8c7ba7d2fa7..f327e947b24 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 cadf936c367..7bf14c9a15c 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 6ebf58724a0..20e05f23054 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 f24abcd06de..c4cc2f3b887 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 f05649727b6..022aa391937 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 7cf29a03ed6..448f4cd0ce3 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 88823810497..1da6f134888 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 b0d7d6a7a4c..10e20d8196d 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 e976fc6bef7..80c8883e54b 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 aeba26dc0a3..fa6d398e97b 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 c4d1aff1fdb..60eeda3057e 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 1c7fe6862a6..d96173ff1db 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 c7c2896bbd8..16a5af30e9d 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 92b2a6db4fd..3c006103c1e 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 50ccf377120..8cbda43727c 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 7df81575b7f..0b2333ee07f 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 9058bed0795..fac1ab23f62 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 8996175cc95..285bc62bbe4 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 5e749c528a6..15d037ae25c 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 eec115bf951..5d7ee8fcdd5 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 8c02fa66159..c7e449f6397 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 ed020f722b0..797e636771d 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 ba06e813c58..9d23b1efd36 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 2b74b0ab147..db813e071ce 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 771adf47e94..32129f3ea83 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 d508b5d0538..26cc0f6699f 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 a03351b6853..4e67f0ed1fc 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 d8ba3a4a876..fba150a6cd2 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 3e1deec67a5..71bd13e22e2 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 4ab8d36831b..c854285a437 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 3d76a415fbd..a653a513641 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 c151bcf5519..71798f0da27 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 02c5ab071d1..442f43f11b7 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 62f70bd5e3c..3092ff1d00a 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 2fe5f125076..ca5173fbf00 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 df789f683e6..73be154f7f0 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 5b9dfa2c51b..8e0160d275c 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 e6273162e12..310dbaba55f 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 179e47049a4..ee43499544c 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 d1391ac5509..1658fe59039 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 9620c67fae7..4482b2c72ce 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 7cc8e9b19fb..5ec5bb9a94d 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 e684108637a..435c083cc54 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 e750cd65c1c..79d11a658bd 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 40e4c3bd2cb..83f076abce3 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 ba3082422a0..f118de6e367 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 b111903aa32..2618cfa592e 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 eec127864fe..2c19cd0113c 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 2d16be2259d..341b101b035 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 1314522a813..3ae71a34082 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 919632b10aa..76e6501d238 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 a0baf2d0ba7..48e1a01718e 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 72c4081feff..2220f956994 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 5748b1e1a12..7f9c7bcf3c8 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 a1b92446c8b..d015bfe0095 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 9276ed99738..b12c60cf5a0 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 e57a6460577..8f0100f67a9 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 f1db54202de..28ab9f9d760 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 6bf104ea051..0d12ace6166 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 6f9945b04e1..c77914d99d1 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 3cf8a8e801e..9da0e1807ff 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 93bfd19dec7..b4628874933 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 9ab712a56ce..9521ce004dc 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 32e536edf09..3d26a30abe1 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 74e1d3075a2..fc24ef05b3f 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 00000000000..968c1994eda
--- /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 00000000000..f0ee46d1554
--- /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 00000000000..60ced589f89
--- /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 00000000000..37bdefdbead
--- /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 00000000000..0923f0e3b3d
--- /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 00000000000..be9084e5eac
--- /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 00000000000..2e0a66575bb
--- /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 00000000000..6faf1fb2161
--- /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 00000000000..a995a780db1
--- /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 982f4463896..0a88c44ace0 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 31e8af0ba27..67a661cf521 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 845be1864f6..5ff9a58b613 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 fac0deba24a..a1f76ee032e 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 0f8ed8461fb..fc50299caa3 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 67a409e60c4..506378a508b 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 363dd2b9475..e5c4e9f5193 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 81cc3b00a07..46b7ad477ce 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 7b60533efe4..88d369708e4 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 0134bec1e39..5394d7a5cfe 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))